標(biāo)題: stm32F407單片機(jī)實(shí)現(xiàn)RS485通訊程序,同時(shí)modbus從機(jī)測(cè)試,用串口3實(shí)現(xiàn) [打印本頁(yè)]

作者: li64331218    時(shí)間: 2023-3-30 22:45
標(biāo)題: stm32F407單片機(jī)實(shí)現(xiàn)RS485通訊程序,同時(shí)modbus從機(jī)測(cè)試,用串口3實(shí)現(xiàn)

自己畫(huà)的板子,用串口3 (PD8,PD9實(shí)現(xiàn),PB15收發(fā)選擇電平)。

stm32F407單片機(jī)源程序如下:
  1. /*485-2 */
  2. #define RS485_RE_GPIO_PORT                  GPIOB
  3. #define RS485_RE_GPIO_PIN                   SYS_GPIO_PIN15
  4. #define RS485_RE_GPIO_CLK_ENABLE()          do{ RCC->AHB1ENR |= 1 << 1; }while(0)   /* PB口時(shí)鐘使能 */

  5. #define RS485_TX_GPIO_PORT                  GPIOD
  6. #define RS485_TX_GPIO_PIN                   SYS_GPIO_PIN8
  7. #define RS485_TX_GPIO_AF                    7                                       /* AF功能選擇 */
  8. #define RS485_TX_GPIO_CLK_ENABLE()          do{ RCC->AHB1ENR |= 1 << 3; }while(0)   /* PD口時(shí)鐘使能 */

  9. #define RS485_RX_GPIO_PORT                  GPIOD
  10. #define RS485_RX_GPIO_PIN                   SYS_GPIO_PIN9
  11. #define RS485_RX_GPIO_AF                    7                                       /* AF功能選擇 */
  12. #define RS485_RX_GPIO_CLK_ENABLE()          do{ RCC->AHB1ENR |= 1 << 3; }while(0)   /* PD口時(shí)鐘使能 */

  13. #define RS485_UX                            USART3
  14. #define RS485_UX_IRQn                       USART3_IRQn
  15. #define RS485_UX_IRQHandler                 USART3_IRQHandler
  16. #define RS485_UX_CLK_ENABLE()               do{ RCC->APB1ENR |= 1 << 18; }while(0)  /* USART3 時(shí)鐘使能 */
  17. /*485-2 END */
  18. /* 控制RS485_RE腳, 控制RS485發(fā)送/接收狀態(tài)
  19. * RS485_RE = 0, 進(jìn)入接收模式
  20. * RS485_RE = 1, 進(jìn)入發(fā)送模式
  21. */
  22. #define RS485_RE(x)     sys_gpio_pin_set(RS485_RE_GPIO_PORT, RS485_RE_GPIO_PIN, x)


  23. #define RS485_REC_LEN               64          /* 定義最大接收字節(jié)數(shù) 64 */
  24. #define RS485_EN_RX                 1           /* 使能(1)/禁止(0)RS485接收 */


  25. extern uint8_t g_RS485_rx_buf[RS485_REC_LEN];   /* 接收緩沖,最大RS485_REC_LEN個(gè)字節(jié) */
  26. extern uint8_t g_RS485_rx_cnt;                  /* 接收數(shù)據(jù)長(zhǎng)度 */


  27. void rs485_init(uint32_t sclk, uint32_t baudrate);  /* RS485初始化 */
  28. void rs485_send_data(uint8_t *buf, uint8_t len);    /* RS485發(fā)送數(shù)據(jù) */
  29. void rs485_receive_data(uint8_t *buf, uint8_t *len);/* RS485接收數(shù)據(jù) */
  30. extern void UartDriver(void); /* 485接收數(shù)據(jù)處理 */

  31. #endif
  32. //串口驅(qū)動(dòng)函數(shù),檢測(cè)數(shù)據(jù)幀的接收,調(diào)度功能函數(shù),需在主循環(huán)中調(diào)用
  33. void UartDriver(void)
  34. {
  35.         unsigned char i=0,cnt;
  36.         unsigned int crc;
  37.         unsigned char crch,crcl;
  38.         static uint8_t len;
  39.         static uint8_t buf[60];
  40.         if(flagFrame)            //幀接收完成標(biāo)志,即接收到一幀新數(shù)據(jù)
  41.         {
  42.                 flagFrame=0;           //幀接收完成標(biāo)志清零
  43.                 len = UartRead(buf,sizeof(buf));   //將接收到的命令讀到緩沖區(qū)中
  44.                 if(buf[0]==0x01)                   //判斷地址是不是0x01
  45.                 {
  46.                         crc=GetCRC16(buf,len-2);       //計(jì)算CRC校驗(yàn)值,出去CRC校驗(yàn)值
  47.                         crch=crc>>8;                                    //crc高位
  48.                         crcl=crc&0xFF;                                        //crc低位
  49.                         if((buf[len-2]==crch)&&(buf[len-1]==crcl))  //判斷CRC校驗(yàn)是否正確
  50.                         {
  51.                                 switch (buf[1])  //按功能碼執(zhí)行操作
  52.                                 {
  53.                                         case 0x03:     //讀數(shù)據(jù)
  54.                                                 if((buf[2]==0x00)&&(buf[3]<=0x05))  //寄存器地址支持0x0000~0x0005
  55.                                                 {
  56.                                                         
  57.                                                         if(buf[3]<=0x04)
  58.                                                         {
  59.                                                                 i=buf[3];//提取寄存器地址
  60.                                                                 cnt=buf[5];  //提取待讀取的寄存器數(shù)量
  61.                                                                 buf[2]=cnt*2;  //讀取數(shù)據(jù)的字節(jié)數(shù),為寄存器*2,因modbus定義的寄存器為16位
  62.                                                                 len=3;                                                        
  63.                                                                 while(cnt--)
  64.                                                                 {
  65.                                                                         buf[len++]=0x00;                                //寄存器高字節(jié)補(bǔ)0
  66.                                                                         buf[len++]=regGroup[i++];                //低字節(jié)
  67.                                                         }
  68.                                                         
  69.                                                 }
  70.                                                         break;
  71.                                         }
  72.                                                 else  //寄存器地址不被支持時(shí),返回錯(cuò)誤碼
  73.                                                 {   
  74.                                                         buf[1]=0x83;  //功能碼最高位置1
  75.                                                         buf[2]=0x02;  //設(shè)置異常碼為02-無(wú)效地址
  76.                                                         len=3;
  77.                                                         break;
  78.                                                 }
  79.                                         case 0x06:           //寫(xiě)入單個(gè)寄存器
  80.                                                 if((buf[2]==0x00)&&(buf[3]<=0x05))   //寄存器地址支持0x0000-0x0005
  81.                                                 {
  82.                                                         if(buf[3]<=0x04)
  83.                                                         {
  84.                                                                 i=buf[3];                                //提取寄存器地址
  85.                                                                 regGroup[i]=buf[5];                //保存寄存器數(shù)據(jù)
  86.                                                                 LED0(0);
  87.                                                         }
  88.                                                         len -=2;                 //長(zhǎng)度-2以重新計(jì)算CRC并返回原幀
  89.                                                         break;
  90.                                                 }
  91.                                                 else  
  92.                                                 {                                                        //寄存器地址不被支持,返回錯(cuò)誤碼
  93.                                                         buf[1]=0x86;           //功能碼最高位置1
  94.                                                         buf[2]=0x02;           //設(shè)置異常碼為02-無(wú)效地址
  95.                                                         len=3;
  96.                                                         break;
  97.                                         }
  98.                                         default:    //其他不支持的功能碼
  99.                                                     buf[1]=0x80;     //功能碼最高位置1
  100.                                                         buf[2]=0x01;     //設(shè)置異常碼為01—無(wú)效功能
  101.                                                         len=3;
  102.                                                         break;
  103.                                 }
  104.                             crc=GetCRC16(buf,len);           //計(jì)算CRC校驗(yàn)值
  105.                                 buf[len++]=crc>>8;           //CRC高字節(jié)
  106.                                 buf[len++]=crc&0xff;        //CRC低字節(jié)
  107. //                                rs485_UartWrite(buf,len);  //發(fā)送響應(yīng)幀
  108.                 rs485_send_data(buf,len);       //發(fā)送響應(yīng)幀
  109.                         }
  110.                 }
  111.         }
  112. }

  113.                                 
  114. void UartRxMonitor(uint8_t ms) //串口接收監(jiān)控
  115. {
  116.         static uint8_t USART3_RX_BKP=0;  //定義USART2_RC_BKP暫時(shí)存儲(chǔ)數(shù)據(jù)長(zhǎng)度與實(shí)際長(zhǎng)度比較
  117.         static uint8_t idletmr=0;        //定義監(jiān)控時(shí)間
  118.         if(g_RS485_rx_cnt>0)//接收計(jì)數(shù)器大于零時(shí),監(jiān)控總線空閑時(shí)間
  119.         {
  120.                 if(USART3_RX_BKP!=g_RS485_rx_cnt) //接收計(jì)數(shù)器改變,即剛接收到數(shù)據(jù)時(shí),清零空閑計(jì)時(shí)
  121.                 {
  122.                         USART3_RX_BKP=g_RS485_rx_cnt;  //賦值操作,將實(shí)際長(zhǎng)度給USART2_RX_BKP
  123.                         idletmr=0;                    //將監(jiān)控時(shí)間清零
  124.                 }
  125.                 else                              ////接收計(jì)數(shù)器未改變,即總線空閑時(shí),累計(jì)空閑時(shí)間
  126.                 {
  127.                         //如果在一幀數(shù)據(jù)完成之前有超過(guò)3.5個(gè)字節(jié)時(shí)間的停頓,接收設(shè)備將刷新當(dāng)前的消息并假定下一個(gè)字節(jié)是一個(gè)新的數(shù)據(jù)幀的開(kāi)始
  128.                         if(idletmr<5)                  //空閑時(shí)間小于1ms時(shí),持續(xù)累加
  129.                         {
  130.                                 idletmr +=ms;
  131.                                 if(idletmr>=5)             //空閑時(shí)間達(dá)到1ms時(shí),即判定為1幀接收完畢
  132.                                 {
  133.                                         flagFrame=1;//設(shè)置命令到達(dá)標(biāo)志,幀接收完畢標(biāo)志
  134.                                 }
  135.                         }
  136.                 }
  137.         }
  138.         else
  139.         {
  140.                 USART3_RX_BKP=0;
  141.         }
  142. }

  143.                         



  144. /**
  145. * @brief       RS485發(fā)送len個(gè)字節(jié)
  146. * @param       buf     : 發(fā)送區(qū)首地址
  147. * @param       len     : 發(fā)送的字節(jié)數(shù)(為了和本代碼的接收匹配,這里建議不要超過(guò) RS485_REC_LEN 個(gè)字節(jié))
  148. * @retval      無(wú)
  149. */
  150. void rs485_send_data(uint8_t *buf, uint8_t len)
  151. {
  152.     uint8_t t;
  153.     RS485_RE(1);    /* 進(jìn)入發(fā)送模式 */

  154.     for (t = 0; t < len; t++)   /* 循環(huán)發(fā)送數(shù)據(jù) */
  155.     {
  156.         while ((RS485_UX->SR & 0X40) == 0); /* 等待發(fā)送結(jié)束 */

  157.         RS485_UX->DR = buf[t];
  158.     }

  159.     while ((RS485_UX->SR & 0X40) == 0);     /* 等待發(fā)送結(jié)束 */

  160.     g_RS485_rx_cnt = 0;
  161.     RS485_RE(0);    /* 進(jìn)入接收模式 */
  162. }

  163. /**
  164. * @brief       RS485查詢(xún)接收到的數(shù)據(jù)
  165. * @param       buf     : 接收緩沖區(qū)首地址
  166. * @param       len     : 接收到的數(shù)據(jù)長(zhǎng)度
  167. *   @arg               0   , 表示沒(méi)有接收到任何數(shù)據(jù)
  168. *   @arg               其他, 表示接收到的數(shù)據(jù)長(zhǎng)度
  169. * @retval      無(wú)
  170. */
  171. void rs485_receive_data(uint8_t *buf, uint8_t *len)
  172. {
  173.     uint8_t rxlen = g_RS485_rx_cnt;
  174.     uint8_t i = 0;
  175.     *len = 0;       /* 默認(rèn)為0 */
  176.     delay_ms(10);   /* 等待10ms,連續(xù)超過(guò)10ms沒(méi)有接收到一個(gè)數(shù)據(jù),則認(rèn)為接收結(jié)束 */

  177.     if (rxlen == g_RS485_rx_cnt && rxlen)   /* 接收到了數(shù)據(jù),且接收完成了 */
  178.     {
  179.         for (i = 0; i < rxlen; i++)
  180.         {
  181.             buf[i] = g_RS485_rx_buf[i];
  182.         }

  183.         *len = g_RS485_rx_cnt;  /* 記錄本次數(shù)據(jù)長(zhǎng)度 */
  184.         g_RS485_rx_cnt = 0;     /* 清零 */
  185.     }
  186. }
復(fù)制代碼

Keil代碼下載: 測(cè)試 485-2(串口3)modbus rtu從機(jī)實(shí)驗(yàn).7z (216.49 KB, 下載次數(shù): 78)

作者: li64331218    時(shí)間: 2023-4-2 16:15
485通訊是半雙工的,注意收發(fā)控制引腳的電平切換。
作者: aayon1979    時(shí)間: 2024-9-2 16:19
好東西!我來(lái)下一個(gè)
作者: rongshengju    時(shí)間: 2024-9-18 08:22
學(xué)習(xí)。!




歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1