自己画的板子,用串口3 (PD8,PD9实现,PB15收发选择电平)。
stm32F407单片机源程序如下:
- /*485-2 */
- #define RS485_RE_GPIO_PORT GPIOB
- #define RS485_RE_GPIO_PIN SYS_GPIO_PIN15
- #define RS485_RE_GPIO_CLK_ENABLE() do{ RCC->AHB1ENR |= 1 << 1; }while(0) /* PB口时钟使能 */
- #define RS485_TX_GPIO_PORT GPIOD
- #define RS485_TX_GPIO_PIN SYS_GPIO_PIN8
- #define RS485_TX_GPIO_AF 7 /* AF功能选择 */
- #define RS485_TX_GPIO_CLK_ENABLE() do{ RCC->AHB1ENR |= 1 << 3; }while(0) /* PD口时钟使能 */
- #define RS485_RX_GPIO_PORT GPIOD
- #define RS485_RX_GPIO_PIN SYS_GPIO_PIN9
- #define RS485_RX_GPIO_AF 7 /* AF功能选择 */
- #define RS485_RX_GPIO_CLK_ENABLE() do{ RCC->AHB1ENR |= 1 << 3; }while(0) /* PD口时钟使能 */
- #define RS485_UX USART3
- #define RS485_UX_IRQn USART3_IRQn
- #define RS485_UX_IRQHandler USART3_IRQHandler
- #define RS485_UX_CLK_ENABLE() do{ RCC->APB1ENR |= 1 << 18; }while(0) /* USART3 时钟使能 */
- /*485-2 END */
- /* 控制RS485_RE脚, 控制RS485发送/接收状态
- * RS485_RE = 0, 进入接收模式
- * RS485_RE = 1, 进入发送模式
- */
- #define RS485_RE(x) sys_gpio_pin_set(RS485_RE_GPIO_PORT, RS485_RE_GPIO_PIN, x)
- #define RS485_REC_LEN 64 /* 定义最大接收字节数 64 */
- #define RS485_EN_RX 1 /* 使能(1)/禁止(0)RS485接收 */
- extern uint8_t g_RS485_rx_buf[RS485_REC_LEN]; /* 接收缓冲,最大RS485_REC_LEN个字节 */
- extern uint8_t g_RS485_rx_cnt; /* 接收数据长度 */
- void rs485_init(uint32_t sclk, uint32_t baudrate); /* RS485初始化 */
- void rs485_send_data(uint8_t *buf, uint8_t len); /* RS485发送数据 */
- void rs485_receive_data(uint8_t *buf, uint8_t *len);/* RS485接收数据 */
- extern void UartDriver(void); /* 485接收数据处理 */
- #endif
- //串口驱动函数,检测数据帧的接收,调度功能函数,需在主循环中调用
- void UartDriver(void)
- {
- unsigned char i=0,cnt;
- unsigned int crc;
- unsigned char crch,crcl;
- static uint8_t len;
- static uint8_t buf[60];
- if(flagFrame) //帧接收完成标志,即接收到一帧新数据
- {
- flagFrame=0; //帧接收完成标志清零
- len = UartRead(buf,sizeof(buf)); //将接收到的命令读到缓冲区中
- if(buf[0]==0x01) //判断地址是不是0x01
- {
- crc=GetCRC16(buf,len-2); //计算CRC校验值,出去CRC校验值
- crch=crc>>8; //crc高位
- crcl=crc&0xFF; //crc低位
- if((buf[len-2]==crch)&&(buf[len-1]==crcl)) //判断CRC校验是否正确
- {
- switch (buf[1]) //按功能码执行操作
- {
- case 0x03: //读数据
- if((buf[2]==0x00)&&(buf[3]<=0x05)) //寄存器地址支持0x0000~0x0005
- {
-
- if(buf[3]<=0x04)
- {
- i=buf[3];//提取寄存器地址
- cnt=buf[5]; //提取待读取的寄存器数量
- buf[2]=cnt*2; //读取数据的字节数,为寄存器*2,因modbus定义的寄存器为16位
- len=3;
- while(cnt--)
- {
- buf[len++]=0x00; //寄存器高字节补0
- buf[len++]=regGroup[i++]; //低字节
- }
-
- }
- break;
- }
- else //寄存器地址不被支持时,返回错误码
- {
- buf[1]=0x83; //功能码最高位置1
- buf[2]=0x02; //设置异常码为02-无效地址
- len=3;
- break;
- }
- case 0x06: //写入单个寄存器
- if((buf[2]==0x00)&&(buf[3]<=0x05)) //寄存器地址支持0x0000-0x0005
- {
- if(buf[3]<=0x04)
- {
- i=buf[3]; //提取寄存器地址
- regGroup[i]=buf[5]; //保存寄存器数据
- LED0(0);
- }
- len -=2; //长度-2以重新计算CRC并返回原帧
- break;
- }
- else
- { //寄存器地址不被支持,返回错误码
- buf[1]=0x86; //功能码最高位置1
- buf[2]=0x02; //设置异常码为02-无效地址
- len=3;
- break;
- }
- default: //其他不支持的功能码
- buf[1]=0x80; //功能码最高位置1
- buf[2]=0x01; //设置异常码为01—无效功能
- len=3;
- break;
- }
- crc=GetCRC16(buf,len); //计算CRC校验值
- buf[len++]=crc>>8; //CRC高字节
- buf[len++]=crc&0xff; //CRC低字节
- // rs485_UartWrite(buf,len); //发送响应帧
- rs485_send_data(buf,len); //发送响应帧
- }
- }
- }
- }
-
- void UartRxMonitor(uint8_t ms) //串口接收监控
- {
- static uint8_t USART3_RX_BKP=0; //定义USART2_RC_BKP暂时存储数据长度与实际长度比较
- static uint8_t idletmr=0; //定义监控时间
- if(g_RS485_rx_cnt>0)//接收计数器大于零时,监控总线空闲时间
- {
- if(USART3_RX_BKP!=g_RS485_rx_cnt) //接收计数器改变,即刚接收到数据时,清零空闲计时
- {
- USART3_RX_BKP=g_RS485_rx_cnt; //赋值操作,将实际长度给USART2_RX_BKP
- idletmr=0; //将监控时间清零
- }
- else ////接收计数器未改变,即总线空闲时,累计空闲时间
- {
- //如果在一帧数据完成之前有超过3.5个字节时间的停顿,接收设备将刷新当前的消息并假定下一个字节是一个新的数据帧的开始
- if(idletmr<5) //空闲时间小于1ms时,持续累加
- {
- idletmr +=ms;
- if(idletmr>=5) //空闲时间达到1ms时,即判定为1帧接收完毕
- {
- flagFrame=1;//设置命令到达标志,帧接收完毕标志
- }
- }
- }
- }
- else
- {
- USART3_RX_BKP=0;
- }
- }
-
- /**
- * @brief RS485发送len个字节
- * @param buf : 发送区首地址
- * @param len : 发送的字节数(为了和本代码的接收匹配,这里建议不要超过 RS485_REC_LEN 个字节)
- * @retval 无
- */
- void rs485_send_data(uint8_t *buf, uint8_t len)
- {
- uint8_t t;
- RS485_RE(1); /* 进入发送模式 */
- for (t = 0; t < len; t++) /* 循环发送数据 */
- {
- while ((RS485_UX->SR & 0X40) == 0); /* 等待发送结束 */
- RS485_UX->DR = buf[t];
- }
- while ((RS485_UX->SR & 0X40) == 0); /* 等待发送结束 */
- g_RS485_rx_cnt = 0;
- RS485_RE(0); /* 进入接收模式 */
- }
- /**
- * @brief RS485查询接收到的数据
- * @param buf : 接收缓冲区首地址
- * @param len : 接收到的数据长度
- * @arg 0 , 表示没有接收到任何数据
- * @arg 其他, 表示接收到的数据长度
- * @retval 无
- */
- void rs485_receive_data(uint8_t *buf, uint8_t *len)
- {
- uint8_t rxlen = g_RS485_rx_cnt;
- uint8_t i = 0;
- *len = 0; /* 默认为0 */
- delay_ms(10); /* 等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束 */
- if (rxlen == g_RS485_rx_cnt && rxlen) /* 接收到了数据,且接收完成了 */
- {
- for (i = 0; i < rxlen; i++)
- {
- buf[i] = g_RS485_rx_buf[i];
- }
- *len = g_RS485_rx_cnt; /* 记录本次数据长度 */
- g_RS485_rx_cnt = 0; /* 清零 */
- }
- }
复制代码
Keil代码下载:
测试 485-2(串口3)modbus rtu从机实验.7z
(216.49 KB, 下载次数: 77)
|