自己畫的板子,用串口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, 下載次數: 78)
2023-3-31 01:21 上傳
點擊文件名下載附件
|