[AT32F421] mcu与威纶HMI的modbus rtu rs485通信

[复制链接]
2583|7
 楼主| nos001 发表于 2022-10-20 15:06 | 显示全部楼层 |阅读模式
本帖最后由 nos001 于 2022-10-20 15:43 编辑

mcu为32f421,威纶HMI为TK6071iP。
威纶提供的C51例程不能用,参考好多个modbus例程才使mcu与HMI正常通信,可以进行位读、写,16bit数据读、写。
通信还算健壮,不会断链。

  1. /**********************************************************************************************************
  2. *   文  件: modbus_rtu_slave.c
  3. *   版  本: v1.0.0
  4. *   编  写:
  5. *   说  明: modbus从站协议
  6. *   版  本:
  7. *   Copyright (C), 2021-2031 xxxx
  8. *********************************************************************************************************/
  9. #include "at32f4xx.h"

  10. #include "modbus_rtu.h"
  11. #include "bsp_usart.h"
  12. #include "queue.h"


  13. uint16_t RTU_M_buf[RTU_M_BUF_SIZE];   // 位寄存器区
  14. uint16_t RTU_D_buf[RTU_D_BUF_SIZE];   // 字寄存器区


  15. extern void modbus_rtu_01H( void );
  16. extern void modbus_rtu_05H( void );
  17. extern void modbus_rtu_03H( void );
  18. extern void modbus_rtu_06H( void );
  19. extern void modbus_rtu_10H( void );

  20. extern void rs485_dma_tx( uint8_t len );
  21. extern void error_back( uint8_t func, uint8_t type );

  22. /*******************************************************************************
  23. * 16位CRC检验表,低位在前,高位在后
  24. *
  25. *
  26. ******************************************************************************/
  27. /* 高位表 */
  28. uint8_t const auchCRCHi[256] =
  29. {
  30.     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
  31.     0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
  32.     0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
  33.     0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
  34.     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
  35.     0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
  36.     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
  37.     0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
  38.     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
  39.     0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
  40.     0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
  41.     0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
  42.     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
  43.     0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
  44.     0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
  45.     0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
  46.     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
  47.     0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
  48.     0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
  49.     0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
  50.     0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
  51.     0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
  52.     0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
  53.     0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
  54.     0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
  55.     0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
  56. };

  57. /* 低位表 */
  58. uint8_t const auchCRCLo[256] =
  59. {
  60.     0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
  61.     0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
  62.     0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
  63.     0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
  64.     0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
  65.     0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
  66.     0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
  67.     0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
  68.     0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
  69.     0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
  70.     0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
  71.     0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
  72.     0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
  73.     0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
  74.     0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
  75.     0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
  76.     0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
  77.     0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
  78.     0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
  79.     0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
  80.     0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
  81.     0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
  82.     0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
  83.     0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
  84.     0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
  85.     0x43, 0x83, 0x41, 0x81, 0x80, 0x40
  86. };

  87. /**************************************************************************************************
  88. * brief  16位CRC校验函数,查表法
  89. * param
  90. * note
  91. *************************************************************************************************/
  92. uint16_t CRC16_calc( uint8_t *puchMsg, uint8_t usDataLen )
  93. {
  94.     uint8_t uchCRCHi = 0xFF ;
  95.     uint8_t uchCRCLo = 0xFF ;
  96.     uint16_t uIndex ;

  97.     while( usDataLen-- )
  98.     {
  99.         uIndex = uchCRCHi ^ *puchMsg++ ;
  100.         uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;
  101.         uchCRCLo = auchCRCLo[uIndex] ;
  102.     }

  103.     return ( ( uint16_t )( uchCRCLo ) << 8 | uchCRCHi ) ;
  104. }

  105. /*
  106. 关于 Modbus 各地址的说明

  107. --------------------------------------------------------------------------------

  108. EasyBuilder Pro 中 Modbus 协议的地址类型为 0x、1x、3x、4x、5x、6x,还有 3x_Bit、4x_Bit 、6x_Bit 等等,下面将分别说明这些地址类型在 Modbus 协议中支持的功能码。

  109. 地址类型  描述
  110. 0x      是个可读可写的地址类型,相当于操作设备的输出点。该地址类型读位状态时的功能码为 01H,写位状态时的功能码为 05H。写多个位寄存器时的功能码为 0fH。
  111. 1x      是个只读的地址类型,相当于读设备的输入点。读位状态时的功能码为 02H。
  112. 3x      是个只读的地址类型,相当于读设备的只读数据寄存器。读数据时的功能码为 04H。
  113. 4x      是个可读可写的地址类型,相当于操作设备的数据寄存器。当读数据时的功能码为 03H,当写数据时的功能码为 10H。
  114. 5x      该地址类型与 4x 属性是一样的。即读写的功能码完全一样。不同之处在于,当为双字时,若 32_bit unsigned 格式的数据,使用 5x 和 4x 两种地址类型分别读取数据时,
  115.         高字和低字的位置是颠倒的。若使用 4x 地址类型读到的数据是 0x1234,则使用 5x 地址类型读取的数据即为 0x3412。
  116. 6x      是一个可读可写的地址类型,读数据时的功能码也是 03H,与 4x 不同之处在于写数据时的功能码为 06H,即写单个寄存器的数据。
  117. 3x_Bit  该地址类型支持的功能码与 3x 地址类型完全一致,不同之处在于 3x 是读数据,而 3x_Bit 是读数据中的某一个 bit 的状态。
  118. 4x_Bit  该地址类型支持的功能码与 4x 地址类型完全一致,不同之处在于 4x 是读数据,而 4x_Bit 是读数据中的某一个 bit 的状态。
  119. 6x_Bit  该地址类型支持的功能码与 6x 地址类型完全一致,不同之处在于 6x 是读数据,而 6x_Bit 是读数据中的某一个 bit 的状态。

  120. */

  121. /**************************************************************************************************
  122. * brief   MODBUS功能码处理
  123. * param
  124. * note    目前只能识别01,03,05,06功能码,而且只能挂载单台
  125. * note    在uart 接收字符间隔超3.5字符的定时器中断中调用
  126. **************************************************************************************************/
  127. void modbus_rtu_analyze( void )
  128. {
  129.     uint16_t crc_val = 0;

  130.     #if 0

  131.     uint8_t  i = 0;
  132.     uint8_t  rece_byte = 8;

  133.     while( Queue_Out( &sQueUart, &UartR_buf[i++] ) )
  134.     {
  135.         rece_byte++;
  136.     }

  137.     #endif

  138.     if( Uart_dma_rxbyte < 8 )  return;                    /* 有效报文的字节数量最少是8个字节 */

  139.     if( UartR_buf[0] == 0x01 )                            /* 站号,本站0x01 */
  140.     {
  141.         /* 接收到的CRC校验码,低字节在前,高字节在后 */
  142.         crc_val = ( UartR_buf[Uart_dma_rxbyte - 1] << 8 ) | UartR_buf[Uart_dma_rxbyte - 2];

  143.         /* CRC 校验正确 */
  144.         if( crc_val == CRC16_calc( UartR_buf, Uart_dma_rxbyte - 2 ) )
  145.         {
  146.             modbus_rtu_01H();
  147.             modbus_rtu_05H();
  148.             modbus_rtu_03H();
  149.             modbus_rtu_06H();
  150.             modbus_rtu_10H();
  151.         }
  152.         else /* 返回校验错误代码 */
  153.         {
  154.             error_back( UartR_buf[1], 0x08 );
  155.         }
  156.     }
  157. }

  158. /**************************************************************************************************
  159. * brief : 01功能码,
  160. * param :
  161. * note  : 位读取,HMI的地址类型0x
  162. **************************************************************************************************/
  163. void modbus_rtu_01H( void )
  164. {
  165.     /* 协议
  166.          主机发送:
  167.          rbuf[0]  01 从机地址
  168.          rbuf[1]  01 功能码
  169.          rbuf[2]  xx 寄存器起始地址高字节
  170.          rbuf[3]  xx 寄存器起始地址低字节
  171.          rbuf[4]  xx 寄存器数量高字节
  172.          rbuf[5]  xx 寄存器数量低字节
  173.          rbuf[6]  xx CRC校验低字节
  174.          rbuf[7]  xx CRC校验高字节

  175.      从机应答:
  176.          tbuf[1]  01 从机地址
  177.          tbuf[2]  01 功能码
  178.          tbuf[3]  xx 返回字节数
  179.          tbuf[4]  xx 数据1
  180.          tbuf[5]  xx 数据2
  181.          tbuf[6]  xx 数据3
  182.          tbuf[7]  xx 数据4
  183.          tbuf[8]  xx 数据5
  184.          tbuf[9]  xx CRC校验高字节
  185.          tbuf[10] xx CRC校验低字节
  186.      */

  187.     uint16_t i, j;
  188.     uint16_t bit_base;
  189.     uint16_t bit_addr;
  190.     uint16_t bit_len;
  191.     uint8_t  bit_baseH, bit_baseL;
  192.     uint8_t  bit_lenH, bit_lenL;
  193.     uint8_t  bit_Val;
  194.     uint16_t crc_val;

  195.     if( UartR_buf[1] == 0x01 )
  196.     {
  197.         bit_baseH = UartR_buf[2];                                        /* 位首址高字节 */
  198.         bit_baseL = UartR_buf[3];                                        /* 位首址低字节 */
  199.         bit_lenH  = UartR_buf[4];                                        /* 位长高字节 */
  200.         bit_lenL  = UartR_buf[5];                                        /* 位长低字节 */

  201.         bit_base  = ( bit_baseH << 8 ) + bit_baseL;                      /* 位首址,16位 */
  202.         bit_len   = ( bit_lenH << 8 ) + bit_lenL;                        /* 位长,16位 */

  203.         if( ( bit_base + bit_len ) >= RTU_M_BUF_SIZE * 16 )              /* 位地址超出范围 */
  204.         {
  205.             // errorsend( 0x01, 0x02 );                                  /*  */
  206.             return;
  207.         }

  208.         UartT_buf[0] = UartR_buf[0];                                     /* 站号 */
  209.         UartT_buf[1] = UartR_buf[1];                                     /* 功能码 */
  210.         UartT_buf[2] = ( bit_len / 8 ) + ( ( bit_len % 8 ) != 0 );       /* 要返回的字节数, 不能整除8的时候要多返回一个字节 */

  211.         for( i = 0; i < UartT_buf[2]; i++ )
  212.         {
  213.             UartT_buf[ 3 + i ] = 0;                                      /* 先清零复位 */

  214.             for( j = 0; j < 8; j++ )                                     /* 每8个位状态组成一个字节返回 */
  215.             {
  216.                 /* RTU_M_buf[] 为16位数组,取8个位状态组成一个字节,算法:位在数组中的单元地址 = 位地址/16, 在16位数据的位 = 位地址 %16 */
  217.                 bit_addr = bit_base + i * 8 + j;
  218.                 bit_Val = ( uint8_t )( RTU_M_buf[ bit_addr / 16 ] >> ( bit_addr % 16 ) ) & 0x01;

  219.                 UartT_buf[ 3 + i ] |= bit_Val << j;                      /* 位读取顺序,低位在前,高位在后;位排列顺序,MSB..LSB */
  220.             }
  221.         }

  222.         crc_val = CRC16_calc( UartT_buf, UartT_buf[2] + 3 );             /* CRC校验 */
  223.                
  224.         UartT_buf[3 + UartT_buf[2] ] = ( uint8_t )( crc_val );           /* CRC低字节 */
  225.         UartT_buf[4 + UartT_buf[2] ] = ( uint8_t )( crc_val >> 8 );      /* CRC高字节 */

  226.         rs485_dma_tx( UartT_buf[2] + 5 );
  227.     }
  228. }

  229. /**************************************************************************************************
  230. * brief  05功能码
  231. * param
  232. * note   写单个位状态,HMI的地址类型0x
  233. **************************************************************************************************/
  234. void modbus_rtu_05H( void )
  235. {
  236.     uint16_t crc_val, bit_addr;

  237.     if( UartR_buf[1] == 0x05 )
  238.     {
  239.         bit_addr = UartR_buf[2] << 8 | UartR_buf[3];          /* 位地址 */

  240.         if( UartR_buf[4] == 0xff )                            /* 强制ON */
  241.         {
  242.             RTU_M_buf[bit_addr / 16] |= 0x0001 << ( bit_addr % 16 );
  243.         }
  244.         else                                                  /* 强制OFF */
  245.         {
  246.             RTU_M_buf[bit_addr / 16] &= ~( 0x0001 << ( bit_addr % 16 ) );
  247.         }

  248.         UartT_buf[0] = UartR_buf[0];                          /* 站号 */
  249.         UartT_buf[1] = UartR_buf[1];                          /* 功能码 */
  250.         UartT_buf[2] = UartR_buf[2];                          /* 起始地址高字节 */
  251.         UartT_buf[3] = UartR_buf[3];                          /* 起始地址低字节 */
  252.         UartT_buf[4] = UartR_buf[4];                          /* 返回状态高字节 */
  253.         UartT_buf[5] = UartR_buf[5];                          /* 返回状态低字节 */

  254.         crc_val = CRC16_calc( UartT_buf, 6 );                 /* 校验 */

  255.         UartT_buf[6] = ( uint8_t )( crc_val );                /* 校验低位 */
  256.         UartT_buf[7] = ( uint8_t )( crc_val >> 8 );           /* 校验高位 */

  257.         rs485_dma_tx( 8 );                                    /* 发送返回 */
  258.     }
  259. }

  260. /**************************************************************************************************
  261. * brief  03功能码
  262. * param
  263. * note   读取多个寄存器,HMI的地址类型6x、4x
  264. * note   读取16位字寄存器区RTU_D_buf
  265. **************************************************************************************************/
  266. void modbus_rtu_03H( void )
  267. {
  268.     uint16_t i = 0, idx = 0;
  269.     uint16_t reg_base, len_words, len_byte;
  270.     uint16_t crc_val;

  271.     if( UartR_buf[1] == 0x03 )
  272.     {
  273.         reg_base  = ( UartR_buf[2] << 8 ) | UartR_buf[3];      /* 寄存器地址 */
  274.         len_words = ( UartR_buf[4] << 8 ) | UartR_buf[5];      /* 读取的字长 */
  275.         len_byte  = len_words * 2;                             /* 字节数 */

  276.         UartT_buf[idx++] = UartR_buf[0];                       /* 站号 */
  277.         UartT_buf[idx++] = UartR_buf[1];                       /* 功能码 */
  278.         UartT_buf[idx++] = ( uint8_t )( len_words * 2 );       /* 字节数 */

  279.         for( i = 0; i < len_words; i++ )                       /* 发送相应字节 */
  280.         {
  281.             UartT_buf[idx++] = ( uint8_t )( RTU_D_buf[reg_base + i] >> 8 ); /* 数据高字节 */
  282.             UartT_buf[idx++] = ( uint8_t )( RTU_D_buf[reg_base + i] );      /* 数据低字节 */
  283.         }

  284.         len_byte = idx;
  285.         crc_val = CRC16_calc( UartT_buf, len_byte );           /* 校验 */

  286.         UartT_buf[idx++] = ( uint8_t )( crc_val );             /* 校验低位 */
  287.         UartT_buf[idx++] = ( uint8_t )( crc_val >> 8 );        /* 校验高位 */

  288.         len_byte = idx;
  289.         rs485_dma_tx( len_byte );                              /* 返回 */
  290.     }
  291. }

  292. /**************************************************************************************************
  293. * brief  06功能码
  294. * param
  295. * note   写单个寄存器,HMI的地址类型6x
  296. **************************************************************************************************/
  297. void modbus_rtu_06H( void )
  298. {
  299.     uint16_t reg_addr, reg_val, crc_val;

  300.     if( UartR_buf[1] == 0x06 )                                 /* 功能码 */
  301.     {
  302.         reg_addr = ( UartR_buf[2] << 8 ) | UartR_buf[3];       /* 写入地址 */
  303.         reg_val  = ( UartR_buf[4] << 8 ) | UartR_buf[5];       /* 寄存器值 */

  304.         RTU_D_buf[reg_addr] = reg_val;                         /* 存入字寄存器区 */

  305.         UartT_buf[0] = UartR_buf[0];                           /* 站号 */
  306.         UartT_buf[1] = UartR_buf[1];                           /* 功能码 */
  307.         UartT_buf[2] = UartR_buf[2];                           /* 寄存器地址高字节 */
  308.         UartT_buf[3] = UartR_buf[3];                           /* 寄存器地址低字节 */
  309.         UartT_buf[4] = UartR_buf[4];                           /* 数据高字节 */
  310.         UartT_buf[5] = UartR_buf[5];                           /* 数据低字节 */

  311.         crc_val = CRC16_calc( UartT_buf, 6 );                  /* 校验 */

  312.         UartT_buf[6] = ( uint8_t )( crc_val );                 /* 校验低位 */
  313.         UartT_buf[7] = ( uint8_t )( crc_val >> 8 );            /* 校验高位 */

  314.         rs485_dma_tx( 8 );                                     /* 返回 */
  315.     }
  316. }

  317. /**************************************************************************************************
  318. * brief  10H功能码
  319. * param
  320. * note   写多个寄存器, HMI的地址类型4x
  321. **************************************************************************************************/
  322. void modbus_rtu_10H( void )
  323. {
  324.     uint16_t reg_base;
  325.     uint16_t reg_num;
  326.     uint16_t crc_val, i;
  327.     uint8_t  w_bytes;

  328.     if( UartR_buf[1] == 0x10 )
  329.     {
  330.         reg_base = ( UartR_buf[2] << 8 ) + UartR_buf[3];       /* 要写入的起始地址 */
  331.         reg_num  = ( UartR_buf[4] << 8 ) + UartR_buf[5];       /* 寄存器数量 */
  332.         w_bytes = UartR_buf[6];                                /* 要写的字节数量 */

  333.         for( i = 0; i < ( w_bytes / 2 ); i++ )                 /* 将值写入寄存器 */
  334.         {
  335.             RTU_D_buf[reg_base + i] = ( UartR_buf[7 + i * 2]  << 8 ) + UartR_buf[8 + i * 2];
  336.         }

  337.         UartT_buf[0] = UartR_buf[0];                           /* 站号 */
  338.         UartT_buf[1] = UartR_buf[1];                           /* 功能码 */
  339.         UartT_buf[2] = UartR_buf[2];                           /* 返回地址高位 */
  340.         UartT_buf[3] = UartR_buf[3];                           /* 返回地址低位 */
  341.         UartT_buf[4] = UartR_buf[4];
  342.         UartT_buf[5] = UartR_buf[5];

  343.         crc_val = CRC16_calc( UartT_buf, 6 );                  /* CRC校验 */

  344.         UartT_buf[6] = ( uint8_t )( crc_val );                 /* CRC低字节 */
  345.         UartT_buf[7] = ( uint8_t )( crc_val >> 8 );            /* CRC高字节 */

  346.         rs485_dma_tx( 8 );
  347.     }
  348. }

  349. #if 1

  350. /*错误返回*/
  351. void error_back( uint8_t func, uint8_t type )
  352. {
  353.     uint16_t crc_val;

  354.     UartT_buf[0] = 0x01;                      /* 返回站号 */

  355.     switch( type )
  356.     {
  357.     case 0x08:
  358.         UartT_buf[1] = 0x80 + func;           /* 返回错误功能码 */
  359.         UartT_buf[2] = 0x08;                  /* 返回错误代码,08:CRC校验错误 */
  360.         break;

  361.     case 0x01:
  362.         UartT_buf[1] = 0x80 + func;           /* 返回错误功能码 */
  363.         UartT_buf[2] = 0x01;                  /* 返回错误代码,01:功能码错误 */
  364.         break;

  365.     case 0x02:
  366.         UartT_buf[1] = 0x80 + func;           /* 返回错误功能码 */
  367.         UartT_buf[2] = 0x02;                  /* 返回错误代码,02:地址错误 */
  368.         break;

  369.     case 0x03:
  370.         UartT_buf[1] = 0x80 + func;           /* 返回错误功能码 */
  371.         UartT_buf[2] = 0x03;                  /* 返回错误代码,03:数据错误 */
  372.         break;

  373.     case 0x04:
  374.         UartT_buf[1] = 0x80 + func;           /* 返回错误功能码 */
  375.         UartT_buf[2] = 0x04;                  /* 返回错误代码,04:不支持的功能码 */
  376.         break;
  377.     }

  378.     crc_val = CRC16_calc( UartT_buf, 3 );     /* CRC校验 */

  379.     UartT_buf[3] = (uint8_t)(crc_val);        /* 校验低字节 */
  380.     UartT_buf[4] = (uint8_t)(crc_val>>8);     /* 校验高字节 */

  381.     rs485_dma_tx(5);
  382. }

  383. #endif





 楼主| nos001 发表于 2022-10-20 15:10 | 显示全部楼层
  1. /**
  2.   **************************************************************************
  3.   * File   : Modbus_rtu.h
  4.   * Version: V1.0
  5.   * Date   :
  6.   * Brief  :
  7.   **************************************************************************
  8.   */
  9.   
  10. #ifndef __MODBUS_RTU_H
  11. #define __MODBUS_RTU_H

  12. #include<stdint.h>


  13. #define RTU_M_BUF_SIZE   128
  14. #define RTU_D_BUF_SIZE   128
  15.   

  16. extern uint16_t RTU_M_buf[RTU_M_BUF_SIZE];   // 位寄存器区
  17. extern uint16_t RTU_D_buf[RTU_D_BUF_SIZE];   // 字寄存器区





  18. extern void modbus_rtu_analyze( void );

  19. #endif




 楼主| nos001 发表于 2022-10-20 15:15 | 显示全部楼层
本帖最后由 nos001 于 2022-10-20 15:24 编辑

采用uart dma接收、发送,待发送数据装填到dma指定的缓存,启动dma发送后不管。uart dma接收,采用串口空闲中断,dam接收使能后串口产生空闲中断,即认为dma完成数据包接收。
  1. /*
  2. *********************************************************************************************************
  3. *
  4. *   程序名称: 串口驱动模块
  5. *   文件名称: bsp_usart.c
  6. *   版    本: v1.0.0
  7. *   编    写:
  8. *   说    明:
  9. *
  10. *   版本记录:
  11. *   v1.0.0: 2021年4月9日,初版
  12. *
  13. *   Copyright (C), 2021-2031, xxxx
  14. *
  15. *********************************************************************************************************
  16. */
  17. #include "at32f4xx.h"

  18. #include "bsp_timer.h"
  19. #include "bsp_usart.h"
  20. #include "modbus_rtu.h"
  21. #include "queue.h"

  22. #include <stdio.h>



  23. uint8_t UartT_buf[UART_BUF_SIZE];    // 发送数据数组
  24. uint8_t UartR_buf[UART_BUF_SIZE];    // 接收数据数组

  25. uint8_t uart_dma_txFull = 0;
  26. uint8_t uart_dma_rxFull = 0;
  27. uint8_t Uart_dma_rxbyte = 0;
  28. uint8_t uart_rx_3_5space = 0;
  29. uint8_t uart_rx_en_tmr = 0;
  30. uint8_t rs485_tx_len;
  31. uint8_t *pUart_tx_buf;




  32. #define UART_TX_DMA_CHANNEL    DMA1_Channel2
  33. #define UART_RX_DMA_CHANNEL    DMA1_Channel3

  34. extern Var_uart_t QueUart_buf[QUEUE_MAX_SIZE];

  35. /**********************************************************************************************************
  36. * brief   RS485控制引脚初始化
  37. * param  
  38. * retval
  39. * note   
  40. **********************************************************************************************************/
  41. static void Uart1_485_control_Init( void )
  42. {
  43.     GPIO_InitType GPIO_InitStructure;

  44.     /* 使能GPIO时钟 */
  45.     RCC_AHBPeriphClockCmd( UART1_485CON_GPIO_RCC, ENABLE );

  46.     /* 配置GPIO */
  47.     GPIO_StructInit( &GPIO_InitStructure );

  48.     GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_2MHz;
  49.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  50.     GPIO_InitStructure.GPIO_OutType = GPIO_OutType_PP;           /* 推挽输出 */
  51.     GPIO_InitStructure.GPIO_Pull = GPIO_Pull_NOPULL;             /* 浮空输入,已外置上拉 */

  52.     GPIO_InitStructure.GPIO_Pins = UART1_485RE_PIN | UART1_485DE_PIN;

  53.     GPIO_Init( UART1_485CON_PORT, &GPIO_InitStructure );
  54. }

  55. /**********************************************************************************************************
  56. * brief   Uart_Init
  57. * param
  58. * retval
  59. * note    配置串口的硬件参数
  60. **********************************************************************************************************/
  61. static void Uart_Init( void )
  62. {
  63.     GPIO_InitType GPIO_InitStructure;
  64.     USART_InitType USART_InitStructure;

  65.     #if USART1_EN
  66.     /* 使能GPIO时钟 */
  67.     RCC_AHBPeriphClockCmd( USART1_GPIO_RCC, ENABLE );
  68.     /* 使能USART1时钟 */
  69.     RCC_APB2PeriphClockCmd( RCC_APB2PERIPH_USART1, ENABLE );

  70.     /* 配置GPIO */
  71.     GPIO_StructInit( &GPIO_InitStructure );
  72.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                                   /* 复用功能 */
  73.     GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;
  74.     /* 配置TX */
  75.     GPIO_InitStructure.GPIO_Pins = USART1_TX_PIN;
  76.     GPIO_InitStructure.GPIO_OutType = GPIO_OutType_PP;
  77.     GPIO_Init( USART1_TX_PORT, &GPIO_InitStructure );
  78.     /* 配置RX */
  79.     GPIO_InitStructure.GPIO_Pull = GPIO_Pull_PU;
  80.     GPIO_InitStructure.GPIO_Pins = USART1_RX_PIN ;
  81.     GPIO_Init( USART1_RX_PORT, &GPIO_InitStructure );

  82.     /* 配置复用功能,具体见参考手册IO复用功能表格6-2 */
  83.     GPIO_PinAFConfig( USART1_TX_PORT, USART1_TX_SOURCE, USART1_TX_AF );
  84.     GPIO_PinAFConfig( USART1_RX_PORT, USART1_RX_SOURCE, USART1_RX_AF );

  85.     /* 配置参数 */
  86.     USART_InitStructure.USART_BaudRate = USART1_BAUD;                               /* 波特率 */
  87.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;                     /* 数据位 */
  88.     USART_InitStructure.USART_StopBits = USART_StopBits_1;                          /* 停止位 */
  89.     USART_InitStructure.USART_Parity = USART_Parity_No;                             /* 检验位 */
  90.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; /* 硬件流控 */
  91.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                 /* 模式,允许发送、接收 */
  92.     USART_Init( USART1, &USART_InitStructure );


  93.     #if USE_USART_DMA_TX

  94.     /* 开启串口DMA发送 */
  95.     usart_dma_enable( USART1, USART_DMAReq_Tx, ENABLE );

  96.     #endif

  97.     #if USE_USART_DMA_RX

  98.     /* 开启串口DMA接收 */
  99.     usart_dma_enable( USART1, USART_DMAReq_Rx, ENABLE );

  100.     #endif   

  101.     #if USE_USART_RX_IDLE

  102.     /* 使能USART1接收空闲、发送完成中断 */
  103.     USART_INTConfig( USART1, USART_INT_IDLEF, ENABLE );
  104.     USART_INTConfig( USART1, USART_INT_TRAC, ENABLE );

  105.     #else

  106.     /* 使能USART1接收、发送中断 */
  107.     USART_INTConfig( USART1, USART_INT_RDNE, ENABLE );
  108.     USART_INTConfig( USART1, USART_INT_TRAC, ENABLE );

  109.     #endif
  110.    

  111.     /* 使能串口 */
  112.     USART_Cmd( USART1, ENABLE );

  113.     #endif

  114.     #if USART2_EN
  115.     /* 使能GPIO时钟 */
  116.     RCC_AHBPeriphClockCmd( USART2_GPIO_RCC, ENABLE );
  117.     /* 使能USART2时钟 */
  118.     RCC_APB1PeriphClockCmd( RCC_APB1PERIPH_USART2, ENABLE );

  119.     /* 配置GPIO */
  120.     GPIO_StructInit( &GPIO_InitStructure );
  121.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; /* 复用功能 */
  122.     GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;
  123.     /* 配置TX */
  124.     GPIO_InitStructure.GPIO_Pins = USART2_TX_PIN;
  125.     GPIO_InitStructure.GPIO_OutType = GPIO_OutType_PP;
  126.     GPIO_Init( USART2_TX_PORT, &GPIO_InitStructure );
  127.     /* 配置RX */
  128.     GPIO_InitStructure.GPIO_Pins = USART2_RX_PIN;
  129.     GPIO_InitStructure.GPIO_Pull = GPIO_Pull_PU;
  130.     GPIO_Init( USART2_RX_PORT, &GPIO_InitStructure );

  131.     /* 配置复用功能------------------------------- */
  132.     GPIO_PinAFConfig( USART2_TX_PORT, USART2_TX_SOURCE, USART2_TX_AF );
  133.     GPIO_PinAFConfig( USART2_RX_PORT, USART2_RX_SOURCE, USART2_RX_AF );

  134.     /* 配置参数 */
  135.     USART_InitStructure.USART_BaudRate = USART2_BAUD;                               /* 波特率 */
  136.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;                     /* 数据位 */
  137.     USART_InitStructure.USART_StopBits = USART_StopBits_1;                          /* 停止位 */
  138.     USART_InitStructure.USART_Parity = USART_Parity_No;                             /* 检验位 */
  139.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; /* 硬件流控 */
  140.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                 /* 模式 */
  141.     USART_Init( USART2, &USART_InitStructure );

  142.     /* 使能串口1接收中断 */
  143.     USART_INTConfig( USART2, USART_INT_RDNE, ENABLE );

  144.     /* 使能串口 */
  145.     USART_Cmd( USART2, ENABLE );

  146.     #endif
  147. }

  148. /**********************************************************************************************************
  149. * brief   NVIC_Uart_Config
  150. * param  
  151. * retval
  152. * note    配置串口硬件中断.
  153. **********************************************************************************************************/
  154. static void NVIC_Uart_Config( void )
  155. {
  156.     NVIC_InitType NVIC_InitStructure;

  157.     #if USART1_EN
  158.     /* 使能USART1中断 */
  159.     NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;         /* IRQ通道 */
  160.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; /* 抢占优先级别 */
  161.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        /* 从优先级别 */
  162.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           /* 使能IRQ通道 */
  163.     NVIC_Init( &NVIC_InitStructure );
  164.     #endif

  165.     #if USART2_EN
  166.     /* 使能USART2中断 */
  167.     NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;         /* IRQ通道 */
  168.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; /* 抢占优先级别 */
  169.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        /* 从优先级别 */
  170.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           /* 使能IRQ通道 */
  171.     NVIC_Init( &NVIC_InitStructure );
  172.     #endif
  173. }

  174. /**********************************************************************************************************
  175. * brief   bsp_UART_SetBaudrate
  176. * param   USARTx.串口 ulValue.波特率
  177. * retval
  178. * note    设置串口波特率,适用于数据8位,1个停止位,无校验位,无硬件流控
  179. **********************************************************************************************************/
  180. void bsp_UART_SetBaudrate( USART_Type *USARTx, uint32_t ulValue )
  181. {
  182.     USART_InitType USART_InitStructure;

  183.     USART_InitStructure.USART_BaudRate = ulValue;                                   /* 波特率 */
  184.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;                     /* 数据位 */
  185.     USART_InitStructure.USART_StopBits = USART_StopBits_1;                          /* 停止位 */
  186.     USART_InitStructure.USART_Parity = USART_Parity_No;                             /* 检验位 */
  187.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; /* 硬件流控 */
  188.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                 /* 模式 */
  189.     USART_Init( USARTx, &USART_InitStructure );
  190. }

  191. /**********************************************************************************************************
  192. * brief  配置串口DMA
  193. * param  
  194. * note   
  195. **********************************************************************************************************/
  196. #define DATA_SIZE_DMA 32

  197. static void uart_dma_configure( void )
  198. {
  199.     DMA_InitType DMA_InitStructure;
  200.     NVIC_InitType NVIC_InitStructure;

  201.     /* DMA clock enable */
  202.     RCC_AHBPeriphClockCmd( RCC_AHBPERIPH_DMA1, ENABLE );

  203.     #if ( USART1_EN && USE_USART_DMA_TX )

  204.     /* dma1 channel2 for usart1 tx configuration */
  205.     DMA_Reset( UART_TX_DMA_CHANNEL );
  206.     DMA_DefaultInitParaConfig( &DMA_InitStructure );
  207.     DMA_InitStructure.DMA_PeripheralBaseAddr = ( uint32_t )&USART1->DT;           /* DMA外设基地址 */
  208.     DMA_InitStructure.DMA_MemoryBaseAddr = ( uint32_t )UartT_buf;                 /* DMA内存基地址 */
  209.     DMA_InitStructure.DMA_Direction = DMA_DIR_MEMORY_TO_PERIPHERAL;               /* 传输方向,外设作为数据传输的目的地 */
  210.     DMA_InitStructure.DMA_BufferSize = DATA_SIZE_DMA;                             /* 数据大小 */
  211.     DMA_InitStructure.DMA_PeripheralInc = DMA_PERIPHERAL_INC_DISABLE;             /* 外设地址寄存器不变 */
  212.     DMA_InitStructure.DMA_MemoryInc = DMA_MEMORY_INC_ENABLE;                      /* 内存地址寄存器递增 */
  213.     DMA_InitStructure.DMA_PeripheralDataWidth = DMA_PERIPHERAL_DATA_WIDTH_BYTE;   /* 外设数据宽度为8位 */
  214.     DMA_InitStructure.DMA_MemoryDataWidth = DMA_MEMORY_DATA_WIDTH_BYTE;           /* 存储器数据宽度为8位 */
  215.     DMA_InitStructure.DMA_Mode = DMA_MODE_NORMAL;                                 /* 非循环模式 */
  216.     DMA_InitStructure.DMA_Priority = DMA_PRIORITY_VERY_HIGH;                      /* 优先级 */
  217.     DMA_InitStructure.DMA_MTOM = DMA_MEMTOMEM_DISABLE;                            /* 非内存到内存 */

  218.     DMA_Init( UART_TX_DMA_CHANNEL, &DMA_InitStructure );

  219.     /* Clear DMA1 Channel2 interrupt Flag */
  220.     dma_clear_flag( DMA1_FLAG_TC2 );

  221.     /* Disable DMA1 Channel2 full data transfer interrupt */
  222.     dma_interrupt_enable( UART_TX_DMA_CHANNEL , DMA_INT_TC, DISABLE );

  223.     /* 上电时禁止dma发送 */
  224.     dma_channel_enable( UART_TX_DMA_CHANNEL, DISABLE );

  225.     #endif

  226.     #if ( USART1_EN && USE_USART_DMA_RX )

  227.     /* dma1 channel3 for usart1 rx configuration */
  228.     DMA_Reset( UART_RX_DMA_CHANNEL );
  229.     DMA_DefaultInitParaConfig( &DMA_InitStructure );
  230.     DMA_InitStructure.DMA_PeripheralBaseAddr = ( uint32_t )&USART1->DT;           /* DMA外设基地址 */
  231.     DMA_InitStructure.DMA_MemoryBaseAddr = ( uint32_t )UartR_buf;                 /* DMA内存基地址 */
  232.     DMA_InitStructure.DMA_Direction = DMA_DIR_PERIPHERAL_TO_MEMORY;               /* 传输方向,外设作为数据传输的目的地 */
  233.     DMA_InitStructure.DMA_BufferSize = DATA_SIZE_DMA;                             /* 数据大小 */
  234.     DMA_InitStructure.DMA_PeripheralInc = DMA_PERIPHERAL_INC_DISABLE;             /* 外设地址寄存器不变 */
  235.     DMA_InitStructure.DMA_MemoryInc = DMA_MEMORY_INC_ENABLE;                      /* 内存地址寄存器递增 */
  236.     DMA_InitStructure.DMA_PeripheralDataWidth = DMA_PERIPHERAL_DATA_WIDTH_BYTE;   /* 外设数据宽度为8位 */
  237.     DMA_InitStructure.DMA_MemoryDataWidth = DMA_MEMORY_DATA_WIDTH_BYTE;           /* 存储器数据宽度为8位 */
  238.     DMA_InitStructure.DMA_Mode = DMA_MODE_CIRCULAR;                               /* 循环模式 */
  239.     DMA_InitStructure.DMA_Priority = DMA_PRIORITY_MEDIUM;                         /* 优先级 */
  240.     DMA_InitStructure.DMA_MTOM = DMA_MEMTOMEM_DISABLE;                            /* 非内存到内存 */

  241.     DMA_Init( UART_RX_DMA_CHANNEL, &DMA_InitStructure );

  242.     /* Disable full data transfer intterrupt */
  243.     dma_interrupt_enable( UART_RX_DMA_CHANNEL, DMA_INT_TC, DISABLE );


  244.     /* 上电时允许dma接收 */
  245.     dma_channel_enable( UART_RX_DMA_CHANNEL, ENABLE );

  246.     #endif

  247.     #if USE_DMA_IRQ

  248.     /* Enable DMA1 channel2、3 IRQ Channel */
  249.     NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel3_2_IRQn;
  250.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
  251.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  252.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  253.     NVIC_Init( &NVIC_InitStructure );

  254.     #endif

  255.     #if USART2_EN == 1
  256.     /* USART2_Tx_DMA_Channel (triggered by USART2 Tx event) Config */
  257.     DMA_Reset( DMA1_Channel4 );
  258.     DMA_DefaultInitParaConfig( &DMA_InitStructure );
  259.     DMA_InitStructure.DMA_PeripheralBaseAddr = ( uint32_t )&USART2->DT;       /* DMA外设基地址 */
  260.     DMA_InitStructure.DMA_MemoryBaseAddr = ( uint32_t )ucaUsar2TxdBuffer;     /* DMA内存基地址 */
  261.     DMA_InitStructure.DMA_Direction = DMA_DIR_MEMORY_TO_PERIPHERAL;           /* 传输方向,外设作为数据传输的目的地 */
  262.     DMA_InitStructure.DMA_BufferSize = DATA_SIZE;                             /* 数据大小 */
  263.     DMA_InitStructure.DMA_PeripheralInc = DMA_PERIPHERAL_INC_DISABLE;          /* 外设地址寄存器不变 */
  264.     DMA_InitStructure.DMA_MemoryInc = DMA_MEMORY_INC_ENABLE;                   /* 内存地址寄存器递增 */
  265.     DMA_InitStructure.DMA_PeripheralDataWidth = DMA_PERIPHERAL_DATA_WIDTH_BYTE; /* 外设数据宽度为8位 */
  266.     DMA_InitStructure.DMA_MemoryDataWidth = DMA_MEMORY_DATA_WIDTH_BYTE;         /* 存储器数据宽度为8位 */
  267.     DMA_InitStructure.DMA_Mode = DMA_MODE_NORMAL;                             /* 非循环模式 */
  268.     DMA_InitStructure.DMA_Priority = DMA_PRIORITY_VERY_HIGH;                   /* 非常高优先级 */
  269.     DMA_InitStructure.DMA_MTOM = DMA_MEMTOMEM_DISABLE;                        /* 非内存到内存 */
  270.     DMA_Init( DMA1_Channel4, &DMA_InitStructure );

  271.     /* Enable DMA1 channel4 IRQ Channel */
  272.     NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_4_IRQn;
  273.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4;
  274.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  275.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  276.     NVIC_Init( &NVIC_InitStructure );

  277.     /* Clear DMA1 Channel4 interrupt Flag */
  278.     dma_clear_flag( DMA1_FLAG_TC4 );
  279.     /* Enable DMA1 Channel4 Transfer Complete interrupt */
  280.     dma_interrupt_enable( DMA1_Channel4, DMA_INT_TC, ENABLE );

  281.     /* Enable USART2 DMA TX Channel */
  282.     dma_channel_enable( DMA1_Channel4, ENABLE );

  283.     /* Enable USART2 DMA TX request */
  284.     usart_dma_enable( USART2, USART_DMAReq_Tx, ENABLE );
  285.     #endif
  286. }

  287. /**********************************************************************************************************
  288. * brief   bsp_uart_init
  289. * param
  290. * note    串口初始化
  291. **********************************************************************************************************/
  292. void bsp_uart_init( void )
  293. {
  294.     Uart_Init();
  295.     uart_dma_configure();
  296.     NVIC_Uart_Config();
  297.     Uart1_485_control_Init();

  298.     /* RS485,DE禁止,RE使能 */
  299.     rs485_tx_disable();
  300.     rs485_rx_enable();
  301. }

  302. /**************************************************************************************************
  303. * brief
  304. * param
  305. * note  
  306. *************************************************************************************************/
  307. void rs485_tx_disable( void )
  308. {
  309.     GPIO_WriteBit( UART1_485CON_PORT, UART1_485DE_PIN, Bit_RESET );
  310. }

  311. /**************************************************************************************************
  312. * brief
  313. * param
  314. * note  
  315. *************************************************************************************************/
  316. void rs485_tx_enable( void )
  317. {
  318.     GPIO_WriteBit( UART1_485CON_PORT, UART1_485DE_PIN, Bit_SET );  /* 高电平有效,高电平允许发送 */
  319. }

  320. /**************************************************************************************************
  321. * brief
  322. * param
  323. * note  
  324. *************************************************************************************************/
  325. void rs485_rx_disable( void )
  326. {
  327.     GPIO_WriteBit( UART1_485CON_PORT, UART1_485RE_PIN, Bit_SET );    /* 低电平有效,允许接收 */
  328. }

  329. /**************************************************************************************************
  330. * brief
  331. * param
  332. * note  
  333. *************************************************************************************************/
  334. void rs485_rx_enable( void )
  335. {
  336.     GPIO_WriteBit( UART1_485CON_PORT, UART1_485RE_PIN, Bit_RESET );  /* 低电平有效,高电平禁止接收 */
  337. }

  338. /**************************************************************************************************
  339. * brief   获取dma通道中断允许位
  340. * param   
  341. * note   
  342. *************************************************************************************************/
  343. uint8_t dma_ien_get( DMA_Channel_Type* chl, uint32_t ie_mask )
  344. {
  345.     return ( ( chl->CHCTRL & ie_mask ) == ie_mask );
  346. }

  347. /**************************************************************************************************
  348. * brief  rs485 dma 发送
  349. * param  len发送字节数
  350. * note   DMA发送数据,要先关闭dma通道,才能设置当前发送的数据单元长度,再开启dma通道。
  351. *************************************************************************************************/
  352. void rs485_dma_tx( uint8_t len )
  353. {
  354.     /* RS485 DE使能,RE禁止 */
  355.     rs485_tx_enable();
  356.     rs485_rx_disable();

  357.     /* Disable USART2 DMA TX Channel,只有通道关闭才能对TCNT赋值 */
  358.     dma_channel_enable( UART_TX_DMA_CHANNEL, DISABLE );
  359.    
  360.     /* 设置当前发送的数据单元长度 */
  361.     DMA_SetCurrDataCounter( UART_TX_DMA_CHANNEL, len );

  362.     /* Enable USART2 DMA TX Channel */
  363.     dma_channel_enable( UART_TX_DMA_CHANNEL, ENABLE );
  364. }


  365. #if USART1_EN
  366. /**********************************************************************************************************
  367. * brief  DMA1_Channel3_2_IRQHandler
  368. * note   串口DMA发送、接收中断;未用
  369. * note   中断进入需要判断dma中断状态位、通道中断允许位,否则乱入。
  370. **********************************************************************************************************/
  371. void DMA1_Channel3_2_IRQHandler( void )
  372. {
  373.     /* dma接收完成 */
  374.     if( dma_flag_get( DMA1_INT_TC3 ) == SET && dma_ien_get( DMA1_Channel3, DMA_INT_TC ) )
  375.     {
  376.         /* 清除中断标志 */
  377.         dma_flag_clear( DMA1_INT_TC3 );

  378.         /* 只有通道关闭才能对寄存器赋值 */
  379.         dma_channel_enable( UART_RX_DMA_CHANNEL, DISABLE );

  380.         /* dma循环队列内存基址更新 */
  381.         sQueUart.front = ( sQueUart.front + 8 ) & ( QUEUE_MAX_SIZE - 1 );
  382.         UART_RX_DMA_CHANNEL->CMBA = (uint32_t)&QueUart_buf[sQueUart.front];

  383.         rs485_rx_disable();

  384.         uart_dma_rxFull = 1;
  385.     }

  386.     /* dma发送完成,并未使能 */
  387.     if( dma_flag_get( DMA1_INT_TC2 ) == SET && dma_ien_get( DMA1_Channel2, DMA_INT_TC ) )
  388.     {
  389.         /* 清除中断标志 */
  390.         dma_flag_clear( DMA1_INT_TC2 );

  391.         uart_dma_txFull = 1;

  392.         /* uart dma 发送完成中断发生后,dma发送并未真正结束,启动定时器延时,等待dma发送结束后才能允许rs485接收 */
  393.        tmr6_reload_start( 1200 );
  394.     }   
  395. }

  396. /**********************************************************************************************************
  397. * brief  USART1中断服务程序
  398. * note   串口字符接收中断、空闲中断只允许一个
  399. * note   空闲中断需同时判断中断状态位、DMA通道允许位
  400. **********************************************************************************************************/
  401. void USART1_IRQHandler( void )
  402. {
  403.     uint8_t clr, receive;

  404.     /* 串口空闲中断,可以做串口dma不定长接收 */
  405.     if( ( USART_GetITStatus( USART1, USART_INT_IDLEF ) != RESET ) && dma_ien_get( DMA1_Channel3, DMA_CHCTRL1_CHEN ) )
  406.     {
  407.         /* 空闲中断清除标志 */
  408.         clr = USART1->STS;                                                         
  409.         clr = USART1->DT;         

  410.         /* 通道关闭,才能对通道寄存器赋值 */
  411.         dma_channel_enable( UART_RX_DMA_CHANNEL, DISABLE );  
  412.         /* 接收的数据长度 */
  413.         Uart_dma_rxbyte = DATA_SIZE_DMA - DMA_GetCurrDataCounter( UART_RX_DMA_CHANNEL );
  414.         /* 重新赋值计数值 */
  415.         DMA_SetCurrDataCounter( UART_RX_DMA_CHANNEL, DATA_SIZE_DMA );  
  416.         dma_channel_enable( UART_RX_DMA_CHANNEL, ENABLE );
  417.         
  418.         rs485_rx_disable();
  419.         uart_dma_rxFull = 1;
  420.         
  421.         TMR_SetCounter( TMR6, 0 );
  422.     }
  423.    
  424.     /* 串口接收字符中断 */
  425.     if( USART_GetITStatus( USART1, USART_INT_RDNE ) != RESET )
  426.     {
  427.         USART_ClearFlag( USART1, USART_FLAG_RDNE );        

  428.         receive = USART_ReceiveData(USART1);
  429.         Queue_In( &sQueUart, &receive );

  430.         /* 接收字符间隔超时计数器清零,超过3.5字符认为是空闲 */
  431.         TMR_SetCounter( TMR6, 0 );
  432.         TMR_SetAutoreload( TMR6, OVER_TIME_35CHAR - 1 );
  433.         TMR_Cmd( TMR6, ENABLE );
  434.                 uart_rx_en_tmr = 1;
  435.     }   


  436.     /* 串口发送完成中断,如果是dma发送,全部数据发送完产生中断,发送期间并不会发生中断 */
  437.     if( USART_GetITStatus( USART1, USART_INT_TRAC ) != RESET )
  438.     {
  439.         USART_ClearFlag( USART1, USART_FLAG_TRAC );

  440.         rs485_tx_disable();
  441.         rs485_rx_enable();        

  442.         TMR_SetCounter( TMR6, 0 );        
  443.     }
  444. }

  445. #endif


  446. #if USART2_EN

  447. /*
  448. *********************************************************************************************************
  449. *   函 数 名: USART1_IRQHandler USART2_IRQHandler
  450. *   功能说明: USART中断服务程序
  451. *   形    参: 无
  452. *   返 回 值: 无
  453. *   note    :中断函数名定义在startup_at32f421c8t7.s
  454. *********************************************************************************************************
  455. */

  456. void USART2_IRQHandler( void )
  457. {
  458.     static uint16_t idx = 0;


  459.     if( USART_GetITStatus( USART2, USART_INT_RDNE ) != RESET )
  460.     {
  461.         /* 清除接收中断标志 */
  462.         USART_ClearFlag( USART1, USART_FLAG_RDNE );


  463.     }
  464. }

  465. #endif





  466. /*
  467. *********************************************************************************************************
  468. *   函 数 名: fputc
  469. *   功能说明: 重定义putc函数,可以使用printf函数从串口1打印输出
  470. *   形    参: 无
  471. *   返 回 值: 无
  472. *********************************************************************************************************
  473. */
  474. int fputc( int ch, FILE *f )
  475. {
  476.     /* 发送数据 */
  477.     USART_SendData( USART1, ( uint16_t )ch );

  478.     /* 等待发送结束 */
  479.     while( USART_GetFlagStatus( USART1, USART_FLAG_TRAC ) == RESET )
  480.     {
  481.     }

  482.     return ch;
  483. }

  484. /*
  485. *********************************************************************************************************
  486. *   函 数 名: fgetc
  487. *   功能说明: 重定义getc函数,可以使用getchar函数从串口1输入数据
  488. *   形    参: 无
  489. *   返 回 值: 无
  490. *********************************************************************************************************
  491. */
  492. int fgetc( FILE *f )
  493. {
  494.     /* 等待接收到数据 */
  495.     while( USART_GetFlagStatus( USART1, USART_FLAG_RDNE ) == RESET )
  496.     {
  497.     }

  498.     return ( int )USART_ReceiveData( USART1 );
  499. }














  500. /********************************************* End of file **********************************************/







 楼主| nos001 发表于 2022-10-20 15:22 | 显示全部楼层
本帖最后由 nos001 于 2022-10-20 15:23 编辑
  1. /*
  2. *********************************************************************************************************
  3. *
  4. *        程序名称: 串口驱动模块
  5. *        文件名称: bsp_usart.h
  6. *        版    本: v1.0.0
  7. *   编    写:
  8. *   日    期: 2021年4月9日
  9. *
  10. *        Copyright (C), 2021-2031, xxxx
  11. *
  12. *********************************************************************************************************
  13. */

  14. #ifndef __BSP_USART_H
  15. #define __BSP_USART_H

  16. #include <stdint.h>


  17. /**
  18. * 串口1的GPIO分布
  19. * TXD:   PA9     PB6
  20. * RXD:   PA10    PB7
  21. */
  22. #define USART1_TX_PORT         GPIOB
  23. #define USART1_TX_PIN          GPIO_Pins_6
  24. #define USART1_TX_SOURCE       GPIO_PinsSource6
  25. #define USART1_TX_AF           GPIO_AF_0

  26. #define USART1_RX_PORT         GPIOB
  27. #define USART1_RX_PIN          GPIO_Pins_7
  28. #define USART1_RX_SOURCE       GPIO_PinsSource7
  29. #define USART1_RX_AF           GPIO_AF_0

  30. #define USART1_GPIO_RCC        RCC_AHBPERIPH_GPIOB

  31. /* 串口2的GPIO分布
  32. * TXD: PA2 PA14
  33. * RXD: PA3 PA15 PB0
  34. *
  35. * 复用功能见参考手册6.3.7
  36. */
  37. #define USART2_TX_PORT         GPIOA
  38. #define USART2_TX_PIN          GPIO_Pins_2
  39. #define USART2_TX_SOURCE       GPIO_PinsSource2
  40. #define USART2_TX_AF           GPIO_AF_1

  41. #define USART2_RX_PORT         GPIOA
  42. #define USART2_RX_PIN          GPIO_Pins_3
  43. #define USART2_RX_SOURCE       GPIO_PinsSource3
  44. #define USART2_RX_AF           GPIO_AF_1

  45. #define USART2_GPIO_RCC        RCC_AHBPERIPH_GPIOA

  46. /**
  47. * UART1 RS485 Control Pin
  48. * RE: PC13    低电平有效
  49. * DE: PC14    高电平有效
  50. */
  51. #define UART1_485CON_PORT      GPIOC
  52. #define UART1_485RE_PIN        GPIO_Pins_13
  53. #define UART1_485DE_PIN        GPIO_Pins_14

  54. #define UART1_485CON_GPIO_RCC  RCC_AHBPERIPH_GPIOC



  55. /* 波特率 */
  56. #define USART1_BAUD            38400
  57. #define USART2_BAUD            115200


  58. /* RTU通信需要确定超时时间 */
  59. #if USART1_BAUD <= 19200
  60.   /* 1.5个字符的超时时间 T = BAUDRATE/11/1000*/
  61.   #define OVER_TIME_15CHAR         (uint16_t)(((float)USART1_BAUD/11)*1.5f)
  62.   /* 3个字符的超时时间 */
  63.   #define OVER_TIME_35CHAR         (uint16_t)(((float)USART1_BAUD/11)*3.5f)
  64. #else
  65.   /* 波特率超过19200bit/s的情况下建议的超时时间 */
  66.   #define OVER_TIME_15CHAR     750u     // 750us
  67.   #define OVER_TIME_35CHAR     1750u    // 1.75ms  
  68.   
  69. #endif



  70. #define USART1_EN              1
  71. #define USART2_EN              0

  72. #define USE_USART_DMA_TX       1
  73. #define USE_USART_DMA_RX       1
  74. #define USE_USART_RX_IDLE      1
  75. #define USE_DMA_IRQ            0


  76. /* variable */
  77. #define UART_BUF_SIZE          64


  78. extern uint8_t UartT_buf[UART_BUF_SIZE];                 // 发送数据数组
  79. extern uint8_t UartR_buf[UART_BUF_SIZE];                 // 接收数据数组

  80. extern uint8_t uart_dma_txFull;
  81. extern uint8_t uart_dma_rxFull;
  82. extern uint8_t Uart_dma_rxbyte;
  83. extern uint8_t uart_rx_3_5space;
  84. extern uint8_t uart_rx_en_tmr;
  85. extern uint8_t rs485_tx_len;
  86. extern uint8_t *pUart_tx_buf;


  87. /* function */
  88. extern void bsp_uart_init(void);

  89. extern void rs485_tx_disable(void);
  90. extern void rs485_tx_enable(void);
  91. extern void rs485_rx_disable(void);
  92. extern void rs485_rx_enable(void);

  93. extern void rs485_dma_tx( uint8_t len );


  94. #endif

  95. /********************************************* End of file **********************************************/







 楼主| nos001 发表于 2022-10-20 15:26 | 显示全部楼层
  1. /*
  2. *********************************************************************************************************
  3. * brief : TMR6_GLOBAL_IRQHandler,
  4. * param :
  5. * note  : TIMR6中断, Uart接收空闲超3.5字符中断
  6. *********************************************************************************************************
  7. */
  8. #if 1

  9. void TMR6_GLOBAL_IRQHandler(void)
  10. {
  11.     if (TMR_GetINTStatus(TMR6, TMR_INT_Overflow) != RESET)
  12.     {
  13.         /* 清零溢出中断标志 */
  14.         TMR_ClearITPendingBit(TMR6, TMR_INT_Overflow);

  15.         TMR_Cmd(TMR6, DISABLE);              /* TMR6停止计数 */
  16.                 TMR_SetCounter( TMR6, 0 );
  17.                 TMR_SetAutoreload( TMR6, 20000 );
  18.                 TMR_Cmd(TMR6, ENABLE);               /* TMR6开始计数 */

  19.         /* 串口接收间隔超过3.5字符 */
  20.                 if( uart_rx_en_tmr )
  21.                 {
  22.                         uart_rx_en_tmr = 0;
  23.             uart_rx_3_5space = 1;
  24.                         rs485_rx_disable();        
  25.                 }
  26.         else  /* 超过2s未产生串口接收中断,使能485接收 */
  27.                 {
  28.                         rs485_tx_disable();
  29.             rs485_rx_enable();               
  30.                 }                       
  31.     }
  32. }

  33. /********************************************* End of file **********************************************/
  34. #endif
 楼主| nos001 发表于 2022-10-20 15:27 | 显示全部楼层
  1. /*
  2. *********************************************************************************************************
  3. *        函 数 名: bsp_timer6_init
  4. *        功能说明: TMR6初始化
  5. *        形    参: 无
  6. *        返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_timer6_init(void)
  10. {
  11.     TMR_TimerBaseInitType TMR_TMReBaseStructure;
  12.     NVIC_InitType NVIC_InitStructure;

  13.     /* 使能TMR6时钟 */
  14.     RCC_APB1PeriphClockCmd(RCC_APB1PERIPH_TMR6, ENABLE);

  15.     /* 配置TMR6 */
  16.     TMR_TimeBaseStructInit(&TMR_TMReBaseStructure);
  17.     TMR_TMReBaseStructure.TMR_Period = OVER_TIME_35CHAR - 1;   /* 自动重装载寄存器值,定时1ms */
  18.     TMR_TMReBaseStructure.TMR_DIV = 120 - 1;                   /* 分频器120分频 TMR6时钟 = 1MHz */
  19.     TMR_TMReBaseStructure.TMR_ClockDivision = 0;               /* TMR6时钟除频 */
  20.     TMR_TMReBaseStructure.TMR_CounterMode = TMR_CounterDIR_Up; /* 向上计数 */
  21.        
  22.     TMR_TimeBaseInit(TMR6, &TMR_TMReBaseStructure);

  23.     /* 清零溢出中断标志 */
  24.     TMR_ClearITPendingBit(TMR6, TMR_INT_Overflow);
  25.     /* 使能溢出中断 */
  26.     TMR_INTConfig(TMR6, TMR_INT_Overflow, ENABLE);

  27.     TMR_Cmd( TMR6, ENABLE );


  28.     /* 使能TMR6全局中断 */
  29.     NVIC_InitStructure.NVIC_IRQChannel = TMR6_GLOBAL_IRQn;    /* IRQ通道 */
  30.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; /* 抢占优先级别 */
  31.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        /* 从优先级别 */
  32.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           /* 使能IRQ通道 */
  33.     NVIC_Init(&NVIC_InitStructure);
  34. }
SILA丶BOTON 发表于 2023-10-28 13:37 | 显示全部楼层
大佬 可以上传个完整的通讯范例学习嘛  小白有几个头文件对应不上
 楼主| nos001 发表于 2023-12-6 23:00 | 显示全部楼层
SILA丶BOTON 发表于 2023-10-28 13:37
大佬 可以上传个完整的通讯范例学习嘛  小白有几个头文件对应不上

完整的工程,水平风格都不好,将就看吧。
获取、写HIMI的位和字寄存器函数:
Get_RTU_D
Get_RTU_M
Wrt_RTU_D
Set_RTU_M

Code_AT32F421.part01.rar

10 MB, 下载次数: 16

Code_AT32F421.part02.rar

1.65 MB, 下载次数: 16

您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

66

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部