/********************************************************/ // 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位 -----------+-------------+-------------------------------------------- | | 0001:300 0111:4800 b7b6b5b4 | 波特率 | 0100:600 1000:9600 | | 0101:1200 1001:19200 | | 0110:2400 -----------+-------------+-------------------------------------------- b8 | | 0:不用 注:无协议通讯专用 -----------+-------------+-------------------------------------------- b9 | | 0:不用 同上 -----------+-------------+-------------------------------------------- b12b11b10 | 通讯接口 | 000:RS485(RS422)接口 | | 010: (RS232)接口 -----------+-------------+-------------------------------------------- b13 | 求和检查 | 0:不加求和码 1:自动加上求和码 -----------+-------------+------------------------------------------- b14 | 协议 | 0:无协议通讯 1:专用通讯协议 -----------+-------------+-------------------------------------------- b15 | 协议格式 | 0:格式1 1:格式4 ---------------------------------------------------------------------- 举例:D8120 = 0X4096 通讯波特率是19200 *********************************************************************************/ #include "stm32f10x.h" #include "stm32f10x_flash.h" #include #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=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=0x600)&&(temp_bit<=0x66E)) //主要是C200-C255以后的地址是32位的 { PLC_16BIT[0xBC8+temp1]=PLC_16BIT[temp_bit+1]; //将需要的数据传到缓存 temp1++; } } monitor2=PLC_16BIT[0XA01]; //读出需要监控位数量 for(monitor1=0;monitor10x7fff) { edit_prog=0; prog_address-=0x8000; for(temp=0;temp(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;temp2addr+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;tempDR);//外设地址 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); } }