赞
踩
目前工业上,传感器一般都选RS485,modbus通讯协议,这种通讯方式,有很强的鲁棒性,本篇文章基于原子哥的精英板进行开发。
1、初始化与电脑通信的串口(PA9 PA10)
- //初始化USART2
- void RS485_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- USART_InitTypeDef USART_InitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD,ENABLE);
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
-
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;//PA2(TX)复用推挽输出
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOA,&GPIO_InitStructure);
- GPIO_SetBits(GPIOA,GPIO_Pin_2);//默认高电平
-
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;//PA3(RX)输入上拉
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //修改原GPIO_Mode_IPU(输入上拉)->GPIO_Mode_IN_FLOATING(浮空输入)/
- GPIO_Init(GPIOA,&GPIO_InitStructure);
-
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;//修改PG9(RE/DE)通用推挽输出->PD7(RE/DE)通用推挽输出//
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
- GPIO_Init(GPIOD,&GPIO_InitStructure);
- GPIO_ResetBits(GPIOD,GPIO_Pin_7);//默认接收状态
-
- USART_DeInit(USART2);//复位串口2
- USART_InitStructure.USART_BaudRate=RS485_Baudrate;
- USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
- USART_InitStructure.USART_WordLength=USART_WordLength_8b;
- USART_InitStructure.USART_StopBits=USART_StopBits_1;
- USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//收发模式
- switch(RS485_Parity)
- {
- case 0:USART_InitStructure.USART_Parity=USART_Parity_No;break;//无校验
- case 1:USART_InitStructure.USART_Parity=USART_Parity_Odd;break;//奇校验
- case 2:USART_InitStructure.USART_Parity=USART_Parity_Even;break;//偶校验
- }
- USART_Init(USART2,&USART_InitStructure);
-
- USART_ClearITPendingBit(USART2,USART_IT_RXNE);
- USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);//使能串口2接收中断
-
- NVIC_InitStructure.NVIC_IRQChannel=USART2_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
- NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
- NVIC_Init(&NVIC_InitStructure);
-
- USART_Cmd(USART2,ENABLE);//使能串口2
- RS485_TX_EN=1;//模式
-
- Timer7_Init();//定时器7初始化,用于监视空闲时间
- //Modbus_RegMap();//Modbus寄存器映射
- }
2、采用串口中断将数据保存到数组buff中
- void RS485_SendData(u8 *buff,u8 len)
- {
- RS485_TX_EN=1;//切换为发送模式
- while(len--)
- {
- while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);//等待发送区为空
- USART_SendData(USART2,*(buff++));
- }
- while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET);//等待发送完成
- TX_RX_SET=1; //发送命令完成,定时器T4处理接收到的数据
- RS485_TX_EN=0;
- }
3、用定时器来配置一帧字节是否结束(空闲时间>指定时间)
- void TIM7_IRQHandler(void)
- {
- if(TIM_GetITStatus(TIM7,TIM_IT_Update)!=RESET)
- {
- TIM_ClearITPendingBit(TIM7,TIM_IT_Update);//清除中断标志
- TIM_Cmd(TIM7,DISABLE);//停止定时器
- RS485_TX_EN=1;//默认为发送模式 485模式控制.0,接收;1,发送.
- RS485_RxFlag=1;//置位帧结束标记
- // errpace=1;
- }
- }
-
-
- 4、主机发送请求指令(从机地址、功能码 、起始地址、读取个数)
-
- void modbus_rtu(void)
- {
- static u8 i=0;
- static u8 j=0;
- switch(i)
- {
- case 0: //modbus执行命令第一步。
- //RS485_TX_Service(); //向从机发送一个请求。就在此时发送完成TX_RX_SET=1 发送命令完成,定时器T4处理接收到的数据
- //在此处也可以直接写Master_Service( SlaverAddr, Fuction, StartAddr, ValueOrLenth);
- //多次通讯结果可以按照类似的封装进行填写
-
- RS485_TX_Service();
- if(TX_RX_SET) i=1; //发送,接受命令切换。 0 发送模式 1 接受模式
-
-
- state=1;
- break;
- case 1: //modbus命令执行第二步。
- RS485_RX_Service(); //执行数据接收
- state=2;
- if(ComErr==0) //如果什么错误都没有发生
- {
- i=2;//完成命令更换功能码!
- } //一次通讯已经完成
- else //错误接收后再次准备接收
- {
- i=1;//
- j++;//一个命令发送3次没有应答切换下一个命令
- if(j>=2)
- {
- j=0;
- i=2;
- ComErr=7; //通讯超时
- }
- }
- break;
- case 2: //从机地址++
- i=0;
- state=3;
- break;
- case 3://功能码,这个是空余出来做报错以及其他处理的
- break;
-
- }
-
- }
5、从机响应,通过校验码判断数据是否成功
- u16 CRC_Compute(u8 *puchMsg, u16 usDataLen)
- {
- u8 uchCRCHi = 0xFF ;
- u8 uchCRCLo = 0xFF ;
- u32 uIndex ;
- while (usDataLen--)
- {
- uIndex = uchCRCHi ^ *puchMsg++ ;
- uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;
- uchCRCLo = auchCRCLo[uIndex] ;
- }
- return ((uchCRCHi<< 8) | (uchCRCLo)) ;
- }//uint16 crc16(uint8 *puchMsg, uint16 usDataLen)
6、处理主机采集到的数据
- void USART2_IRQHandler(void)//串口2中断服务程序
- {
-
- u8 res;
- u8 err;
-
- if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)
- {
- if(USART_GetFlagStatus(USART2,USART_FLAG_NE|USART_FLAG_FE|USART_FLAG_PE)) {err=1;errpace=2;}//检测到噪音、帧错误或校验错误
- else err=0;
- res=USART_ReceiveData(USART2); //读接收到的字节,同时相关标志自动清除
-
- if((RS485_RX_CNT<2047)&&(err==0))
- {
- RS485_RX_BUFF[RS485_RX_CNT]=res;
- RS485_RX_CNT++;
-
- TIM_ClearITPendingBit(TIM7,TIM_IT_Update);//清除定时器溢出中断
- TIM_SetCounter(TIM7,0);//当接收到一个新的字节,将定时器7复位为0,重新计时(相当于喂狗)
- TIM_Cmd(TIM7,ENABLE);//开始计时
- }
- }
- }
-
- void Modbus_03_Solve(void)
- {
- u8 i;
- //u8 RegNum;
- //RegNum= RS485_RX_BUFF[2]/2;//获取字节数 6---->?
- if(1)//寄存器地址+数量在范围内
- {
- for(i=0;i<20;i++)
- {
- Master_ReadReg[StartAddr+i]= RS485_RX_BUFF[3+i*2]; /高8位
- Master_ReadReg[StartAddr+i]= RS485_RX_BUFF[4+i*2]+(Master_ReadReg[StartAddr+i]<<8);// 低8位+高8位
- Master_ReadReg[i]= RS485_RX_BUFF[3+i*2];
- Master_ReadReg[i]= RS485_RX_BUFF[4+i*2]+(Master_ReadReg[i]<<8);// 低8位+高8位
-
-
- }
- temp=Master_ReadReg[0];
- x_shock=Master_ReadReg[1];
- y_shock=Master_ReadReg[2];
- z_shock=Master_ReadReg[3];
- //volback(message);
- ComErr=0;
- }
- else
- {
-
- ComErr=3;
- }
- TX_RX_SET=0; //命令完成
- }
-
- //Modbus功能码05处理程序 ///程序已验证OK
- //写单个输出开关量
- void Modbus_05_Solve(void)
- {
- u16 i;
- i=ValueOrLenth;
- if((i>0&&RS485_RX_BUFF[4]==0XFF&&RS485_RX_BUFF[5]==0X00)||(i==0&&RS485_RX_BUFF[4]==0X00&&RS485_RX_BUFF[5]==0X00))
- {
- ComErr=0;
-
- }
- else
- {
- ComErr=5;
- }
- TX_RX_SET=0; //命令完成
- }
-
- //Modbus功能码06处理程序 //已验证程序OK
- //写单个保持寄存器
- void Modbus_06_Solve(void)
- {
- u16 i; //数据返回校验用
- i=(((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
- if(i==Master_WriteReg[StartAddr])
- {
- ComErr=0;
- }
- else
- {
- ComErr=6;
- }
- TX_RX_SET=0; //命令完成
-
- }
- //Modbus功能码15处理程序 //程序已验证OK
- //写多个输出开关量
- void Modbus_15_Solve(void)
- {
- u16 i;//数据返回校验用
- i=(((u16)RS485_RX_BUFF[4])<<8)|RS485_RX_BUFF[5];//获取寄存器数量
- if(i==ValueOrLenth)
- {
- ComErr=0;
- }
- else
- {
- ComErr=15;
- }
- TX_RX_SET=0; //命令完成
- }
-
- //返回温度值
- u16 temperature(void)
- {
- return temp;
- }
- //返回x 振动
- u16 X_shock(void)
- {
- return x_shock;
- }
-
- //返回y 振动
- u16 Y_shock(void)
- {
- return y_shock;
- }
- //返回z 振动
- u16 Z_shock(void)
- {
- return z_shock;
- }
7、结果展示
有什么疑问或想要完整程序可私聊我 ,平时回消息都比较快
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。