xiaoxiaocheng_plc/users/PLC_Com.c

996 lines
38 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/********************************************************/
// CPU需要STM32F103--RAM内存不小于64K Flash内存不小于128K
// 本代码已在STM32F103RDT6、VET6测试通过
// 编辑日期20150909
// editor by 小小晟
// 网店shop182385147.taobao.com
/********************************************************
PLC相关的特殊寄存器
专用辅助继电器 描述
M8126 全局标志
M8127 通讯请求握手信号
M8128 出错标志
M8129 通讯请求切换
专用数据寄存器 描述
D8000 = 200; 扫描时间
D8001 = 0X5EF6; 型号版本 FX2N(C)
D8101 = 0X5EF6; 型号版本 FX2N(C)
D8002 = 8; 内存容量
D8102 = 8; 内存容量
D8003 = 0x0010; 内存类型、寄存器类型
D8006 CPU电池电压
D8010 = 10; 扫描当前值
D8011 = 20; 扫描最小时间(0.1MS)
D8012 = 140; 扫描最长时间(0.1MS)
D6030 D6031 D6032 D6033 是模拟量输入
D8080 D8081 是模拟输出
D8120 = 0X4096 通讯格式
D8121 从站号最多16个
D8127 交换数据的首地址
D8128 交换数据量
D8129 网络通讯超时时间确认值
D8000 看门狗
D8019 对应星期
D8018 对应年份
D8017 对应月份
D8016 对应日期
D8015 对应小时
D8014 对应分钟
D8013 对应秒
通讯格式详解D8120
----------------------------------------------------------------------
位号 | 含 义 | 描述
-----------+-------------+--------------------------------------------
b0 | 数据长度 | 0 7位 1 8位
-----------+-------------+--------------------------------------------
b2b1 | 校验方式 | 00不用 01奇校验 11偶校验
-----------+-------------+--------------------------------------------
b3 | 停止位 | 0 1位 1 2位
-----------+-------------+--------------------------------------------
| | 0001300 01114800
b7b6b5b4 | 波特率 | 0100600 10009600
| | 01011200 100119200
| | 01102400
-----------+-------------+--------------------------------------------
b8 | | 0不用 注:无协议通讯专用
-----------+-------------+--------------------------------------------
b9 | | 0不用 同上
-----------+-------------+--------------------------------------------
b12b11b10 | 通讯接口 | 000RS485RS422接口
| | 010 RS232接口
-----------+-------------+--------------------------------------------
b13 | 求和检查 | 0不加求和码 1自动加上求和码
-----------+-------------+-------------------------------------------
b14 | 协议 | 0无协议通讯 1专用通讯协议
-----------+-------------+--------------------------------------------
b15 | 协议格式 | 0格式1 1格式4
----------------------------------------------------------------------
举例D8120 = 0X4096 通讯波特率是19200
*********************************************************************************/
#include "stm32f10x.h"
#include "stm32f10x_flash.h"
#include <stdio.h>
#include "PLC_Dialogue.h"
#include "PLC_IO.h"
#include "PLC_CONF.H"
// #define flash_start_address 0x8006000
const u8 PLC_ROM[34000] __at (PLC_RAM_ADDR)={
//FLASH起始地址为PLC信息**************************最前的0X02表示PLC为16K的程序步,密码区域和差数区域****************
0x10,0x00,0xD8,0xBA,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0xF4,0x09,0xFF,0x0B,0xF4,0x01,0xE7,0x03,0x64,0x0E,0xC7,0x0E,0xDC,0x0E,0xFF,0x0E,0x90,0x01,0xFE,0x03,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0X0F,0X00,//结束指令
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,};
const u16 special_d[256]={
0X00C8,0X5EF6,0X0010,0X0010,0X0000,0X0025,0X001E,0X0000,0XFFFF,0X0000,
0X0000,0X0000,0X0000,0X00A5,0X00A5,0X00A5,0X00A5,0X00A5,0X0811,0X000F,
0X000A,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,
0X003D,0X001C,0X0000,0X0000,0X0014,0X00FF,0X03D7,0X0000,0X0000,0X0000,
0XFFFF,0XFFFF,0XFFFF,0XFFFF,0XFFFF,0XFFFF,0XFFFF,0XFFFF,0X0000,0XFFFF,
0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,
0X0000,0X0000,0X183B,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,
0X01F4,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,
0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,
0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,
0X0064,0X5EF6,0X0010,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,
0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0DDC,0X3DB6,0X0000,0X0000,
0X0000,0X0000,0X0000,0X0000,0X0002,0X0003,0X0000,0X0000};
const char Ascll[20]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0X41,0X42,0X43,0X44,0X45,0X46};
const char hex[]={
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,
0,10,11,12,13,14,15,0,0,0,0,0,0,};
u8 step_status[1000] __at (0x2000D200);
const u8 p_x[2] __at (0X8005000)={0x08,0x00}; // PLC_RAM_ADDR
u16 prog_write_buffer[2050];
static u8 Flag_Uart_Send=1; //发送标志位
u16 rx_count,tx_count; //数据计算
char tx_data[500]; //发送缓存 143
char rx_data[500]; //接收缓存 143
u16 prog_address,data_address; //计算数据操作起始地址缓存
u16 PLC_16BIT[12100] __at (0x20001000); //PLC_RAM运行数据
u8 PLC_8BIT[] __at (0x20000FFC); //PLC_RAM运行数据
u8 Step_Address[2000]; //写步状态为0上升延与下降沿使用共2K字节1600bit
u8 data_size,block_contol[2];
extern u8 edit_prog;
extern void RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);
u8 Send_out;
u8 Write_Pro_flag = 0;
void data_init(void) //D8000~D8126初始化
{ //
u16 temp; //
u16 temp_address; //
prog_address=0x52; //
for(temp=0;temp<126;temp++) //
{ //
temp_address=0x0700; //取D8000起始地址0E00/2=0X700 使用32位传送
PLC_16BIT[temp_address+temp]=special_d[temp]; //传入系统FLASH备份的用户数据
} //
PLC_16BIT[0X2000]=PLC_ROM[prog_address]; //取字节高位
PLC_16BIT[0X2000]*=256; //
PLC_16BIT[0X2000]|=PLC_ROM[prog_address+1]; //取字节低位
PLC_8BIT(0x01E0)=0x09; //令M8000 M8003置ON
block_contol[0]=200; //防止写参数时程序丢失
block_contol[1]=200; //
} //
void write_block(u16 number) //写入FLASH
{
u16 temp,wait_write,appoint_address;
if(number<17) //写入参数的块必需10块
{
FLASH_Unlock(); //flash关闭保护
FLASH_ErasePage(PLC_RAM_ADDR+number*0x800); //擦除一块数据占用2K
for(temp=0;temp<1024;temp++) //操作为16bit,只需要1024次程序操作完毕
{
appoint_address=PLC_RAM_ADDR+number*0x800+temp*2; //起始地址加上块地址再加上块的小地址,等于目标位置
wait_write=prog_write_buffer[temp*2]+prog_write_buffer[temp*2+1]*0X100; //写入16bit至flash
FLASH_ProgramHalfWord(appoint_address,wait_write); //等待程序写入结束
}
FLASH_Lock(); //结束程序写入开启flash保护
}
}
void backup_block(u16 number) //程序块备份,目的在写程序之前进前面程序备份
{
u16 temp,appoint_address;
if(number<17)
{
for(temp=0;temp<2048;temp++)
{
appoint_address=number*0x800+temp; //起始地址加上块地址再加上块的小地址
prog_write_buffer[temp]=PLC_ROM[appoint_address]; //将程序备份出来
}
}
}
void TX_Process(void) //发送串口数据
{
if(Flag_Uart_Send)
{
Send_out=Flag_Uart_Send = 0;
DMA1_Channel4->CNDTR = tx_count;//设置传输数据长度
DMA1_Channel4->CCR |= 0x0001; //打开DMA
}
}
//=======================================================================================================
// 函数名称: ErasurePLC
// 功能描述PLC擦除FLASH空间
// 输 入: mode 模式
// 输 出: void
// 全局变量:
// 调用模块:
// 作 者: 小小晟
// 日 期: 2014年5月18日
// 备 注:
//-------------------------------------------------------------------------------------------------------
// 修改人:
// 日 期:
// 备 注:
//-------------------------------------------------------------------------------------------------------
//=======================================================================================================
void ErasurePLC(u8 mode)
{
u16 temp=0,Erasure_PLC_16BIT;
/*******************************************PLC存储内存清理 ************************************************/
if(mode==1)
{
backup_block(0);
prog_write_buffer[92]=0x0f; //赋值
prog_write_buffer[93]=0x00; //赋值
for(temp=94;temp<2048;temp++) //从0x5E
{
prog_write_buffer[temp]=0xffff; //
}
FLASH_Unlock(); //flash关闭保护
FLASH_ErasePage(PLC_RAM_ADDR+0x800); //
write_block(0); //备份第一块flash
for(temp=1;temp<10;temp++) //擦除10块
FLASH_ErasePage(PLC_RAM_ADDR+temp*0x800); //擦除flash
FLASH_Lock(); //结束程序写入开启flash保护
}
/*******************************************PLC清理数据元件位*********************************************/
if(mode==2)
{
for(Erasure_PLC_16BIT=0x2000;Erasure_PLC_16BIT<0x7E7E;Erasure_PLC_16BIT+=2) // 清除D0000-D7999
PLC_8BIT(Erasure_PLC_16BIT)=0x00;
}
/*******************************************PLC清理位元件 ***********************************************/
if(mode==3)
{
for(Erasure_PLC_16BIT=0x0000;Erasure_PLC_16BIT<0x00BE;Erasure_PLC_16BIT+=2) // 清除M0000-M3071
PLC_8BIT(Erasure_PLC_16BIT)=0x00;
}
tx_data[1]=0x06,tx_count=1; // 清除完毕报告上位机
}
/*******************************************************************************
函数功能:计算校验和
*******************************************************************************/
u8 check(char *MyD_str)//计算接收区和校验
{
u16 temp;
u8 sum;
sum=0; //请除和记算器
MyD_str+=3; //计算和从第三位开始
for(temp=3;temp<(rx_count-1);temp++)//计算和
{
sum+=*MyD_str; //开始相加
MyD_str++; //指针加一
}
return sum; //数据正常
}
/*******************************************************************************
函数名称void switch_read_data(void)
函数功能转换ASCII码为HEX码占用数据发送寄存器
出口参数:无
********************************************************************************/
void switch_read_data(void)
{
u16 temp;
for(temp=4;temp<(rx_count-2);temp++)
{
tx_data[temp/2]=hex[rx_data[temp]]*0x10;
tx_data[temp/2]+=hex[rx_data[temp+1]];
temp++;
}
}
void setup_HL(void) //高低位交换再转换 ,小端转换
{
u8 temp;
temp=tx_data[3]; //地址高位送入16位数据区
prog_address=temp*0x100+tx_data[2]; //计算程序操作起始地址
}
void setup_LH(void) //正常地址转换
{
u8 temp;
temp=tx_data[3]; //地址高位送入16位数据区
data_address=temp*0x100+tx_data[4]; //计算数据操作起始地址
}
typedef union
{
int data;
char data1[2];
} usart_data;
void read_plc_tyte(u8 addr) //读取PLC型号   //指令“30”
{
u16 temp;
u8 temp_sum;
usart_data plc_type;
plc_type.data=special_d[addr]; //PLC型号
tx_data[1]=0x02; //报文开始 02
temp_sum=0;
for(temp=0;temp<data_size;temp++)
{
tx_data[temp*2+2]=Ascll[plc_type.data1[temp]/0x10]; //取字节高位
tx_data[temp*2+3]=Ascll[plc_type.data1[temp]%0x10]; //取字节低位
temp_sum+=tx_data[temp*2+2]+tx_data[temp*2+3];
}
tx_data[temp*2+2]=0x03; //报文结束 03
temp_sum+=0x03;
tx_data[temp*2+3]=Ascll[temp_sum/0x10];
tx_data[temp*2+4]=Ascll[temp_sum%0x10];
tx_count=temp*2+4;
}
/*******************************************************************************
函数名称<EFBFBD>PLC_Comm_Byte
函数功能:通信字节地址重定义及执行
入口参数Cmd命令
出口参数:映射的实际地址(16BIT地址)
********************************************************************************/
u16 PLC_Comm_Byte(u16 comm_add)
{
if(comm_add>=0x4000&&comm_add<=0x7E7F){return comm_add-0x2000;}//D0000-D7999
else if(comm_add>=0x0280&&comm_add<=0x02FC){return comm_add-0x0280;}//S000-S999
else if(comm_add>=0x0240&&comm_add<=0x0256){return comm_add-0x01C0;}//X000-X277
else if(comm_add>=0x0180&&comm_add<=0x0196){return comm_add-0x00E0;}//Y000-Y267
else if(comm_add>=0x0200&&comm_add<=0x021F){return comm_add-0x0140;}//T00-T255 OVER触点
else if(comm_add>=0x0500&&comm_add<=0x051F){return comm_add-0x0240;}//T00-T255 Enable 线圈
else if(comm_add>=0x01E0&&comm_add<=0x01FF){return comm_add-0x0020;}//C00-C255 OVER触点
else if( comm_add<=0x00BF){return comm_add+0x0100;}//M0000-M1535
else if(comm_add>=0x00C0&&comm_add<=0x017F){return comm_add+0x0440;}//M1536-M3071
else if(comm_add>=0x01C0&&comm_add<=0x01DF){return comm_add+0x0020;}//M8000-M8255
else{return comm_add;} //无效地址
}
/*******************************************************************************
函数名称PLC_Com_BIT
函数功能:通信位地址重定义及执行
入口参数Cmd命令
出口参数:映射的实际地址(BIT地址)
********************************************************************************/
u16 PLC_Com_BIT(u16 addr)
{
if((addr<=0x05FF)) return (addr+0x0800); //M0000-M1535
else if((addr>=0x0600)&&(addr<=0x0BFF)) return (addr+0x2200); //M1536-M3071
else if((addr>=0x0C00)&&(addr<=0x0CB7)) return (addr-0x0700); //Y00-Y267
else if((addr>=0x1200)&&(addr<=0x12BF)) return (addr-0x0E00); //X00-X267
else if((addr>=0x1400)&&(addr<=0x17E7)) return (addr-0X1400); //S00-S999
else if((addr>=0x2800)&&(addr<=0x28FF)) return (addr-0X1200); //T00-T255 Enable 线圈
else if((addr>=0x1000)&&(addr<=0x10FF)) return (addr-0X0A00); //T00-T255 OVER 触点
else if((addr>=0x0E00)&&(addr<=0x0EFF)) return (addr+0X0100); //M8000-M8255
else if((addr>=0x0F00)&&(addr<=0x0FFF)) return (addr-0X0100); //C00-C255 OVER触点
else return addr;
}
//=======================================================================================================
// 函数名称: void READ_data(void)
// 功能描述: 读数据 X,Y,M,S,T,C,D
// 输 入: void
// 输 出: void
// 全局变量:
// 调用模块:
// 作 者: 小小晟
// 日 期: 2014年6月10日
// 备 注:
//-------------------------------------------------------------------------------------------------------
// 修改人:
// 日 期:
// 备 注:
//=======================================================================================================
void read_other_data(void) //指令“30”
{
u16 temp;
u8 temp_sum;
tx_data[1]=0x02; //报文开始
temp_sum=0;
for(temp=0;temp<data_size;temp++)
{
tx_data[temp*2+2]=Ascll[PLC_8BIT(PLC_Comm_Byte(temp+prog_address))/0x10];//取字节高位
tx_data[temp*2+3]=Ascll[PLC_8BIT(PLC_Comm_Byte(temp+prog_address))%0x10];//取字节低位
temp_sum+=tx_data[temp*2+2]+tx_data[temp*2+3];
}
tx_data[temp*2+2]=0x03; //报文结束 03
temp_sum+=0x03;
tx_data[temp*2+3]=Ascll[temp_sum/0x10];
tx_data[temp*2+4]=Ascll[temp_sum%0x10];
tx_count=temp*2+4;
}
//=======================================================================================================
// 函数名称: void READ_data(void)
// 功能描述: 写数据 X,Y,M,S,T,C,D
// 输 入: void
// 输 出: void
// 全局变量:
// 调用模块:
// 作 者: 小小晟
// 日 期: 2014年6月10日
// 备 注:
//-------------------------------------------------------------------------------------------------------
// 修改人:
// 日 期:
// 备 注:
//=======================================================================================================
void PC_WRITE_byte(void) //写字
{
u16 temp;
prog_address=tx_data[2]*0x100+tx_data[3]+4;//计算数据操作起始地址
for(temp=0;temp<data_size;temp++)
{
PLC_8BIT(PLC_Comm_Byte(temp+prog_address))=tx_data[5+temp];
}
tx_data[1]=0x06,tx_count=1; //报告上位机
}
//=======================================================================================================
// 函数名称: void FORCE_ON_data(void)
// 功能描述: FORCE ON X,Y,M,S,T,C
// 输 入: void
// 输 出: void
// 全局变量:
// 调用模块:
// 作 者: 小小晟
// 日 期: 2014年6月10日
// 备 注:
//-------------------------------------------------------------------------------------------------------
// 修改人:
// 日 期:
// 备 注:
//=======================================================================================================
void PC_FORCE_ON(void) //强制 38 ON
{
PLC_BIT_ON(PLC_Com_BIT(tx_data[2]*0x100+tx_data[3]));//计算数据操作起始地址
tx_data[1]=0x06,tx_count=1;
}
//======================================================================================================
// 函数名称: void FORCE_ON_data(void)
// 功能描述: FORCE OFF X,Y,M,S,T,C
// 输 入: void
// 输 出: void
// 全局变量:
// 调用模块:
// 作 者: 小小晟
// 日 期: 2014年6月10日
// 备 注:
//------------------------------------------------------------------------------------------------------
// 修改人:
// 日 期:
// 备 注:
//=======================================================================================================
void PC_FORCE_OFF(void) //强制 37 OFF
{
PLC_BIT_OFF(PLC_Com_BIT(tx_data[2]*0x100+tx_data[3]));//计算数据操作起始地址
tx_data[1]=0x06,tx_count=1;
}
void PC_READ_byte(void) //读字
{
prog_address=tx_data[2]*0x100+tx_data[3]; //计算数据操作起始地址
switch(prog_address)
{
case 0x0ECA: read_plc_tyte(101); break; //读PLC TYPE
case 0x0E02: read_plc_tyte(1); break; //读PLC TYPE
default: read_other_data(); break;
}
}
void EPC_FORCE_ON(void) //使用扩展功能"E"强制ON
{
switch(prog_address)
{
case 0x0E23: break; // 远程操作请求是否可以进行
case 0x0E24: PLC_8BIT(0X01E0)=0x09; Write_Pro_flag = 0; break; // 远程操作需要运行
case 0x0E25: PLC_8BIT(0X01E0)=0x0A; break; // 远程操作需要停止
default: PLC_8BIT(0X01E0)=0x09; PLC_BIT_ON(PLC_Com_BIT(prog_address)); break; // 其它操作区域
}
tx_data[1]=0x06,tx_count=1; //报告上位机
}
void EPC_FORCE_OFF(void) //使用扩展功能"E"强制OFF
{
PLC_BIT_OFF(PLC_Com_BIT(prog_address));
tx_data[1]=0x06,tx_count=1; //报告上位机
}
void PC_READ_Parameter(void) //读配置 E00
{
u16 temp,temp_bit,temp_addr,mov_bit,temp1;
u8 temp_sum;
u8 send,monitor,monitor1,monitor2;
tx_data[1]=0x02; //报文开始
temp_sum=0;
prog_address=PLC_Comm_Byte(tx_data[3]*0x100+tx_data[4]); //计算数据操作起始地址
if((prog_address==0x1790)||(prog_address==0x17D0)) //请求读监控数据区0X1790 与 0X17D0地址
{
if(prog_address==0x1790)
{
monitor1=PLC_16BIT[0XA00]; //读出需要监控字数量 0X1400/2=0XA00
for(temp1=monitor=0;monitor<monitor1;monitor++) //读监控字数据
{
temp_bit=PLC_Comm_Byte(PLC_16BIT[0XA02+monitor])/2;
PLC_16BIT[0xBC8+temp1]=PLC_16BIT[temp_bit];temp1++; //将需要的数据传到缓存 0X1790/2=0XBC8
if((temp_bit>=0x600)&&(temp_bit<=0x66E)) //主要是C200-C255以后的地址是32位的
{
PLC_16BIT[0xBC8+temp1]=PLC_16BIT[temp_bit+1]; //将需要的数据传到缓存
temp1++;
}
}
monitor2=PLC_16BIT[0XA01]; //读出需要监控位数量
for(monitor1=0;monitor1<monitor2;monitor1++) //读监控位数据
{
temp_addr=PLC_Com_BIT(PLC_16BIT[0XA02+monitor+monitor1]);
temp_bit=PLC_16BIT[temp_addr/0x10];
mov_bit = temp_addr%0x10;
if((temp_bit&(1<<mov_bit))==(1<<mov_bit))
PLC_16BIT[0xBC8+temp1+monitor1/0x10]|=1<<(monitor1%0x10);//串口缓存数据
else
PLC_16BIT[0xBC8+temp1+monitor1/0x10]&=~(1<<(monitor1%0x10));
}
}
}
for(temp=0;temp<data_size;temp++) //读RAM
{
send=PLC_8BIT(prog_address+temp);
tx_data[temp*2+2]=Ascll[send/0x10]; //取字节高位
tx_data[temp*2+3]=Ascll[send%0x10]; //取字节低位
temp_sum+=tx_data[temp*2+2]+tx_data[temp*2+3];
}
tx_data[temp*2+2]=0x03; //
temp_sum+=0x03;
tx_data[temp*2+3]=Ascll[temp_sum/0x10];
tx_data[temp*2+4]=Ascll[temp_sum%0x10];
tx_count=temp*2+4;
}
void PC_WRITE_Parameter(void) //写配置 E10
{
u16 temp;
prog_address=tx_data[3]*0x100+tx_data[4]; //计算数据操作起始地址
for(temp=0;temp<data_size;temp++) //write RAM
{
PLC_8BIT(PLC_Comm_Byte(prog_address+temp))=tx_data[6+temp];
}
tx_data[1]=0x06,tx_count=1; //报告上位机
}
void PC_READ_PORG(void) //读程序 E01
{
u16 temp;
u8 temp_sum;
tx_data[1]=0x02; //报文开始
temp_sum=0;
data_address-=0x8000; //读FLASH 地址减0X8000等于实际位置
for(temp=0;temp<data_size;temp++)
{
tx_data[temp*2+2]=Ascll[PLC_ROM[data_address+temp]/0x10]; //取字节高位
tx_data[temp*2+3]=Ascll[PLC_ROM[data_address+temp]%0x10]; //取字节低位
temp_sum+=tx_data[temp*2+2]+tx_data[temp*2+3];
}
tx_data[temp*2+2]=0x03; //
temp_sum+=0x03;
tx_data[temp*2+3]=Ascll[temp_sum/0x10];
tx_data[temp*2+4]=Ascll[temp_sum%0x10];
tx_count=temp*2+4;
}
//void PC_WRITE_PORG(void) //写程序 E11
//{
// u16 temp;
// prog_address=tx_data[3]*0x100+tx_data[4]; //计算数据操作起始地址
// edit_prog=0; //把程序编辑清除,因为写程序时可能存在P地址发生变化PLC应计算P地址
// prog_address-=0x8000; //地址减0X8000等于实际位置
// for(temp=0;temp<data_size;temp++)
// {
// block_contol[0]=(prog_address+temp)/0x800; //每一块占用的地址 0X8002K 字节
// if(block_contol[0]==block_contol[1]) //是否需要跳块,
// {
// prog_write_buffer[(prog_address+temp)-block_contol[0]*0x800]=tx_data[6+temp]; //将数据写入缓存中
// }
// else //需要跳块处理
// {
// write_block(block_contol[1]); //将前一块写入到FLASH
// backup_block(block_contol[0]); //备份需要写的FLASH块
// block_contol[1]=block_contol[0];
// prog_write_buffer[(prog_address+temp)-block_contol[0]*0x800]=tx_data[6+temp];
// }
// }
// tx_data[1]=0x06,tx_count=1;
//}
void PC_WRITE_PORG(void) //ADD 小小晟 20151118
{
u16 temp;
prog_address=(tx_data[3]*0x100+tx_data[4]);
PLC_8BIT(0X01E0)=0x0A;
Write_Pro_flag = 1; //防止在下载过程中程序运行 ADD 小小晟
if(prog_address>0x7fff)
{
edit_prog=0;
prog_address-=0x8000;
for(temp=0;temp<data_size;temp++)
{
block_contol[0]=(prog_address+temp)/0x800;
if(block_contol[0]==block_contol[1])
{
prog_write_buffer[(prog_address+temp)-block_contol[0]*0x800]=tx_data[6+temp];
}
else
{
write_block(block_contol[1]);
backup_block(block_contol[0]);
block_contol[1]=block_contol[0];
prog_write_buffer[(prog_address+temp)-block_contol[0]*0x800]=tx_data[6+temp];
}
}
}
else
{
prog_address+=0x04;
for(temp=0;temp<data_size;temp++)
{
PLC_8BIT(PLC_Comm_Byte(temp+prog_address))=tx_data[6+temp];
}
}
tx_data[1]=0x06,tx_count=1;
// Write_Pro_flag = 0;
}
static u16 find_data(u16 addr,u16 find_data) //查找数据地址,并返回找到的数据地址
{
u8 find_ok,data_H,data_L;
find_ok=5;
data_H=find_data/0x100;
data_L=find_data%0x100;
addr-=0x8000;
do{
if((PLC_ROM[addr]==data_L)&&(PLC_ROM[addr+1]==data_H))
find_ok=0; //找到需要的指令
else
addr+=2;
if(addr>(0xdedb-0x8000))
find_ok=1; //在有效的范围内没有找到END指令
}while(find_ok>3);
addr+=0X8000;
return addr;
}
void find_data_address(void) //查找上位机需要的指令地址
{
u8 temp_sum,data_H,data_L;
data_L=tx_data[5]; //需要查找数据的内容低位
data_H=tx_data[6]; //需要查找数据的内容高位
data_address=find_data(data_address,data_H*0X100+data_L); //读FLASH
tx_data[1]=0x02; //报文开始
temp_sum=0;
tx_data[2]=0x31;
temp_sum+=tx_data[2];
data_H=data_address/0x100;
data_L=data_address%0x100;
tx_data[3]=Ascll[data_H/0X10];
tx_data[4]=Ascll[data_H%0X10];
tx_data[5]=Ascll[data_L/0X10];
tx_data[6]=Ascll[data_L%0X10];
tx_data[7]=0X03;
temp_sum+=tx_data[3];
temp_sum+=tx_data[4];
temp_sum+=tx_data[5];
temp_sum+=tx_data[6];
temp_sum+=tx_data[7];
tx_data[8]=Ascll[temp_sum/0x10];
tx_data[9]=Ascll[temp_sum%0x10];
tx_count=9;
}
void backup_mov_block(u16 number)
{
u16 temp,appoint_address;
if(number<10)
{
for(temp=0;temp<2048;temp++)
{
appoint_address=number*0x800+temp;
prog_write_buffer[temp]=p_x[appoint_address];
}
}
}
void mov_flash(u16 addr,u8 mov_addr)
{
u16 start_addr,end_addr,backup_addr,temp,temp1,temp2,mov_byte,addr_mov;
static u8 offset;
offset=mov_addr;
end_addr=find_data(addr+0x8000,0x000f)+mov_addr-0x8000;
start_addr=end_addr;
addr_mov=addr;
if(addr>0x5B)
{
addr_mov-=0X5C;
end_addr-=0x5C;
addr_mov/=2;
end_addr/=2;
addr_mov/=8;
end_addr/=8;
offset/=2;
mov_byte=offset/8;
offset%=8;
while(!(end_addr==addr_mov))
{
temp=step_status[end_addr]*0x100+step_status[end_addr-1];
temp<<=offset;
step_status[end_addr+mov_byte]=temp/0x100;
end_addr--;
}
temp=step_status[end_addr]*0x100+step_status[end_addr-1];
temp<<=offset;
step_status[end_addr+mov_byte]=temp/0x100;
}
end_addr=start_addr;
temp=start_addr;
do{
if((end_addr/0x800)==(addr/0x800))
start_addr=addr%0x800;
else
start_addr=0;
if((temp/0x800)==(end_addr/0x800))
temp1=end_addr%0x800+1;
else
temp1=2048;
backup_block(end_addr/0x800);
for(temp2=start_addr;temp2<temp1+1;temp2++)
{
backup_addr=(end_addr/0x800)*0x800+temp2-mov_addr;
prog_write_buffer[temp2]=p_x[backup_addr];
}
write_block(end_addr/0x800);
end_addr-=(temp1-start_addr);
}while(end_addr>addr+mov_addr);
}
void online_write_data(void)
{
u16 temp;
u8 temp1,temp2;
temp1=tx_data[5];
temp2=tx_data[6];
temp2-=temp1;
if(temp2>0)
{
mov_flash(data_address-0x8000,temp2);
}
edit_prog=0;
block_contol[0]=100;
block_contol[1]=100;
prog_address=(tx_data[3]*0x100+tx_data[4])-0x8000;
data_size=tx_data[6];
for(temp=0;temp<data_size;temp++)
{
block_contol[0]=(prog_address+temp)/0x800;
if(block_contol[0]==block_contol[1])
{
prog_write_buffer[(prog_address+temp)-block_contol[0]*0x800]=tx_data[7+temp];
}
else
{
write_block(block_contol[1]);
backup_block(block_contol[0]);
block_contol[1]=block_contol[0];
prog_write_buffer[(prog_address+temp)-block_contol[0]*0x800]=tx_data[7+temp];
}
}
write_block(block_contol[0]);
tx_data[1]=0x06,tx_count=1;
}
void all_flash_unlock(void) //FLASH全部解锁
{
block_contol[1]=200;
block_contol[0]=200;
tx_data[1]=0x06,tx_count=1;
}
void all_flash_lock(void) //FLASH全部加锁
{
write_block(block_contol[1]); //加锁之前把需要写的数据写到FLASH
block_contol[1]=200;
block_contol[0]=200;
FLASH_Lock();
tx_data[1]=0x06,tx_count=1;
}
void PC_OPTION_PROG(void) //扩展功能 "E" 代码
{
u16 temp;
if((rx_count==11)&&((rx_data[4]==0x37)||(rx_data[4]==0x38)))//是否为强制功能
{
prog_address=hex[rx_data[5]]*0x10+hex[rx_data[6]]+hex[rx_data[7]]*0x1000+hex[rx_data[8]]*0x100;
if(rx_data[4]==0x37)
{
EPC_FORCE_ON();
}
else
EPC_FORCE_OFF();
}
else
{
setup_LH(); //调用算出地址函数
temp=tx_data[2];
switch(temp)
{
case 0x00: PC_READ_Parameter(); break; //读配置 E00
case 0x10: PC_WRITE_Parameter(); break; //写配置 E10
case 0x01: PC_READ_PORG(); break; //读程序 E01
case 0x11: PC_WRITE_PORG(); break; //写程序 E11
case 0x77: all_flash_unlock(); break; //使用E指令进行写程序写请求77
case 0x87: all_flash_lock(); break; //使用E指令进行写程序结束请求87
case 0x41: find_data_address(); break; //查找END指令地址
case 0x61: all_flash_unlock(); break; //PLC存储内存清理 应为下发了多次 我在这FLASH全部解锁
case 0x60: ErasurePLC(1); break; //PLC存储内存清理
case 0x63: ErasurePLC(2); break; //PLC清理数据元件位
case 0x62: ErasurePLC(3); break; //PLC清理位元件
case 0xD1: online_write_data(); break;
default: tx_data[1]=0x15,tx_count=1;break; //遇到不支持的命令
}
}
}
void find_end(void) //查找程序中是否存在END指令
{
if(rx_count==13)
tx_data[1]=0x06,tx_count=1;
else
tx_data[1]=0x06,tx_count=1;
}
void Process_switch(void)
{
u8 temp;
switch_read_data(); //把第三位开始的ASCII码转换成HEX ,地址为数据发送区
temp=rx_data[3];
switch(temp)
{
case 0x30: data_size=tx_data[4],PC_READ_byte(); break; //代入请求据数长度位“tx_data[4]” 读数据
case 0x31: data_size=tx_data[4];PC_WRITE_byte(); break; //代入请求据数长度位“tx_data[4]” 写数据
case 0x34: find_end(); break; //查找指令如查找到有数据则返回6
case 0x37: setup_HL(),PC_FORCE_ON(); break; //PLC 启动 远程 “0x37”
case 0x38: setup_HL(),PC_FORCE_OFF(); break; //PLC 停止 远程 “0x38”
case 0x42: all_flash_lock(); break; //写参数结束命令
case 0x45: data_size=tx_data[5],PC_OPTION_PROG();break; //E功能指令
default: break;
}
if((tx_count==0)&&(rx_count==0)) //return error code for 0x15
tx_data[1]=0x15,tx_count=1;
rx_count=0;Send_out=1; //报告上位机
}
void usart(u16 DEFAULT_BAUD)
{
USART_InitTypeDef USART_InitStructure; //串口配置寄存器
USART_InitStructure.USART_BaudRate = DEFAULT_BAUD; //设置波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //8位数据位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //一位停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_Init(USART1,&USART_InitStructure); //初始化串口
}
//---------------------串口功能配置---------------------
void USART1_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure; //DMA初始化结构体
GPIO_InitTypeDef GPIO_InitStructure; //串口引脚配置寄存器
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE); //打开串口对应的外设时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //启动DMA时钟
DMA_DeInit(DMA1_Channel4); //DMA1通道4配置
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DR);//外设地址
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)tx_data+1; //内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //dma传输方向单向
DMA_InitStructure.DMA_BufferSize = 143; //设置DMA在传输时缓冲区的长度
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//设置DMA的外设递增模式一个外设
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //设置DMA的内存递增模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据字长
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;//内存数据字长
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //设置DMA的传输模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //设置DMA的优先级别
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //设置DMA的2个memory中的变量互相访问
DMA_Init(DMA1_Channel4,&DMA_InitStructure);
DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
usart(19200); //初始化参数
//TXE发送中断,TC传输完成中断,RXNE接收中断,PE奇偶错误中断,可以是多个
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);//采用DMA方式发送
USART_Cmd(USART1, ENABLE); //启动串口
//*********************串口1的管脚初始化 ****************************************
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //管脚9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //选择GPIO响应速度
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //TX初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //管脚10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //RX初始化
}
void DMA1_Channel4_IRQHandler(void) //串口1DMA方式发送中断
{
DMA1->IFCR |= DMA1_FLAG_TC4; //清除标志位
DMA1_Channel4->CCR &= 0xFFFE; //关闭DMA
Flag_Uart_Send = 1; //允许再次发送
}
void RX_Process(void) //接收串口数据
{
static u8 sum,f=1;
rx_data[0]=0x7f&USART1->DR;
if(rx_data[0]==0X05) //上位机提出通讯请求
rx_count=0,tx_data[1]=0x06,tx_count=1,TX_Process(); //报告上位机并且返回0X06应答
else if(rx_data[0]==0X02) //报文开始
{rx_count=0x01;}
else if(rx_count==0)
{
if(f==1)
{
usart(19200);
rx_count=0,tx_data[1]=0x06,tx_count=1,TX_Process();//报告上位机并且返回0X06应答
f=0;
}
else if(f==0)
{
usart(9600);
rx_count=0,tx_data[1]=0x06,tx_count=1,TX_Process();//报告上位机并且返回0X06应答
f=1;
}
}
if(rx_count>0) //确认报文开始
{
rx_count++;
rx_data[rx_count]=rx_data[0];
if(rx_count>400) //读取数据误差大于143
tx_count=0,rx_count=0;
if((rx_count>3)&&(rx_data[rx_count-2]==0x03)) //数据是否传送结束
{
sum = check(rx_data);
if((rx_data[rx_count-1] == Ascll[sum/0x10])&&(rx_data[rx_count] == Ascll[sum%0x10]))// 计算数据和状态 数据是否正常
{
Process_switch();
}
else
tx_data[1]=0x15,tx_count=1,TX_Process(); //报告上位机并且回答数据异常返值0X15
}
}
}
void USART1_IRQHandler(void) //中断调用
{
if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET) //接收中断
{
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
RX_Process();
}
//溢出-如果发生溢出需要先读SR,再读DR寄存器则可清除不断入中断的问题
if(USART_GetFlagStatus(USART1,USART_FLAG_ORE)==SET)
{
USART_ClearFlag(USART1,USART_FLAG_ORE); //读SR其实就是清除标志
USART_ReceiveData(USART1); //读DR
}
if(USART_GetITStatus(USART1, USART_IT_TXE)==SET)
{
USART_ClearITPendingBit(USART1,USART_IT_TXE);
USART_ITConfig(USART1,USART_IT_TXE,DISABLE);
}
if(USART_GetITStatus(USART1, USART_IT_TC)==SET) //发送中断
{
USART_ClearITPendingBit(USART1,USART_IT_TC);
}
}