打印
[AT32F421]

mcu与威纶HMI的modbus rtu rs485通信

[复制链接]
2121|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
nos001|  楼主 | 2022-10-20 15:06 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 nos001 于 2022-10-20 15:43 编辑

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

/**********************************************************************************************************
*   文  件: modbus_rtu_slave.c
*   版  本: v1.0.0
*   编  写:
*   说  明: modbus从站协议
*   版  本:
*   Copyright (C), 2021-2031 xxxx
*********************************************************************************************************/
#include "at32f4xx.h"

#include "modbus_rtu.h"
#include "bsp_usart.h"
#include "queue.h"


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


extern void modbus_rtu_01H( void );
extern void modbus_rtu_05H( void );
extern void modbus_rtu_03H( void );
extern void modbus_rtu_06H( void );
extern void modbus_rtu_10H( void );

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

/*******************************************************************************
* 16位CRC检验表,低位在前,高位在后
*
*
******************************************************************************/
/* 高位表 */
uint8_t const auchCRCHi[256] =
{
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};

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

/**************************************************************************************************
* brief  16位CRC校验函数,查表法
* param
* note
*************************************************************************************************/
uint16_t CRC16_calc( uint8_t *puchMsg, uint8_t usDataLen )
{
    uint8_t uchCRCHi = 0xFF ;
    uint8_t uchCRCLo = 0xFF ;
    uint16_t uIndex ;

    while( usDataLen-- )
    {
        uIndex = uchCRCHi ^ *puchMsg++ ;
        uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;
        uchCRCLo = auchCRCLo[uIndex] ;
    }

    return ( ( uint16_t )( uchCRCLo ) << 8 | uchCRCHi ) ;
}

/*
关于 Modbus 各地址的说明

--------------------------------------------------------------------------------

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

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

*/

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

    #if 0

    uint8_t  i = 0;
    uint8_t  rece_byte = 8;

    while( Queue_Out( &sQueUart, &UartR_buf[i++] ) )
    {
        rece_byte++;
    }

    #endif

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

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

        /* CRC 校验正确 */
        if( crc_val == CRC16_calc( UartR_buf, Uart_dma_rxbyte - 2 ) )
        {
            modbus_rtu_01H();
            modbus_rtu_05H();
            modbus_rtu_03H();
            modbus_rtu_06H();
            modbus_rtu_10H();
        }
        else /* 返回校验错误代码 */
        {
            error_back( UartR_buf[1], 0x08 );
        }
    }
}

/**************************************************************************************************
* brief : 01功能码,
* param :
* note  : 位读取,HMI的地址类型0x
**************************************************************************************************/
void modbus_rtu_01H( void )
{
    /* 协议
         主机发送:
         rbuf[0]  01 从机地址
         rbuf[1]  01 功能码
         rbuf[2]  xx 寄存器起始地址高字节
         rbuf[3]  xx 寄存器起始地址低字节
         rbuf[4]  xx 寄存器数量高字节
         rbuf[5]  xx 寄存器数量低字节
         rbuf[6]  xx CRC校验低字节
         rbuf[7]  xx CRC校验高字节

     从机应答:
         tbuf[1]  01 从机地址
         tbuf[2]  01 功能码
         tbuf[3]  xx 返回字节数
         tbuf[4]  xx 数据1
         tbuf[5]  xx 数据2
         tbuf[6]  xx 数据3
         tbuf[7]  xx 数据4
         tbuf[8]  xx 数据5
         tbuf[9]  xx CRC校验高字节
         tbuf[10] xx CRC校验低字节
     */

    uint16_t i, j;
    uint16_t bit_base;
    uint16_t bit_addr;
    uint16_t bit_len;
    uint8_t  bit_baseH, bit_baseL;
    uint8_t  bit_lenH, bit_lenL;
    uint8_t  bit_Val;
    uint16_t crc_val;

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

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

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

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

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

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

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

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

        rs485_dma_tx( UartT_buf[2] + 5 );
    }
}

/**************************************************************************************************
* brief  05功能码
* param
* note   写单个位状态,HMI的地址类型0x
**************************************************************************************************/
void modbus_rtu_05H( void )
{
    uint16_t crc_val, bit_addr;

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

        if( UartR_buf[4] == 0xff )                            /* 强制ON */
        {
            RTU_M_buf[bit_addr / 16] |= 0x0001 << ( bit_addr % 16 );
        }
        else                                                  /* 强制OFF */
        {
            RTU_M_buf[bit_addr / 16] &= ~( 0x0001 << ( bit_addr % 16 ) );
        }

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

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

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

        rs485_dma_tx( 8 );                                    /* 发送返回 */
    }
}

/**************************************************************************************************
* brief  03功能码
* param
* note   读取多个寄存器,HMI的地址类型6x、4x
* note   读取16位字寄存器区RTU_D_buf
**************************************************************************************************/
void modbus_rtu_03H( void )
{
    uint16_t i = 0, idx = 0;
    uint16_t reg_base, len_words, len_byte;
    uint16_t crc_val;

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

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

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

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

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

        len_byte = idx;
        rs485_dma_tx( len_byte );                              /* 返回 */
    }
}

/**************************************************************************************************
* brief  06功能码
* param
* note   写单个寄存器,HMI的地址类型6x
**************************************************************************************************/
void modbus_rtu_06H( void )
{
    uint16_t reg_addr, reg_val, crc_val;

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

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

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

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

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

        rs485_dma_tx( 8 );                                     /* 返回 */
    }
}

/**************************************************************************************************
* brief  10H功能码
* param
* note   写多个寄存器, HMI的地址类型4x
**************************************************************************************************/
void modbus_rtu_10H( void )
{
    uint16_t reg_base;
    uint16_t reg_num;
    uint16_t crc_val, i;
    uint8_t  w_bytes;

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

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

        UartT_buf[0] = UartR_buf[0];                           /* 站号 */
        UartT_buf[1] = UartR_buf[1];                           /* 功能码 */
        UartT_buf[2] = UartR_buf[2];                           /* 返回地址高位 */
        UartT_buf[3] = UartR_buf[3];                           /* 返回地址低位 */
        UartT_buf[4] = UartR_buf[4];
        UartT_buf[5] = UartR_buf[5];

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

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

        rs485_dma_tx( 8 );
    }
}

#if 1

/*错误返回*/
void error_back( uint8_t func, uint8_t type )
{
    uint16_t crc_val;

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

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

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

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

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

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

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

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

    rs485_dma_tx(5);
}

#endif





使用特权

评论回复
沙发
nos001|  楼主 | 2022-10-20 15:10 | 只看该作者
/**
  **************************************************************************
  * File   : Modbus_rtu.h
  * Version: V1.0
  * Date   :
  * Brief  :
  **************************************************************************
  */
  
#ifndef __MODBUS_RTU_H
#define __MODBUS_RTU_H

#include<stdint.h>


#define RTU_M_BUF_SIZE   128
#define RTU_D_BUF_SIZE   128
  

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





extern void modbus_rtu_analyze( void );

#endif




使用特权

评论回复
板凳
nos001|  楼主 | 2022-10-20 15:15 | 只看该作者
本帖最后由 nos001 于 2022-10-20 15:24 编辑

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

#include "bsp_timer.h"
#include "bsp_usart.h"
#include "modbus_rtu.h"
#include "queue.h"

#include <stdio.h>



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

uint8_t uart_dma_txFull = 0;
uint8_t uart_dma_rxFull = 0;
uint8_t Uart_dma_rxbyte = 0;
uint8_t uart_rx_3_5space = 0;
uint8_t uart_rx_en_tmr = 0;
uint8_t rs485_tx_len;
uint8_t *pUart_tx_buf;




#define UART_TX_DMA_CHANNEL    DMA1_Channel2
#define UART_RX_DMA_CHANNEL    DMA1_Channel3

extern Var_uart_t QueUart_buf[QUEUE_MAX_SIZE];

/**********************************************************************************************************
* brief   RS485控制引脚初始化
* param  
* retval
* note   
**********************************************************************************************************/
static void Uart1_485_control_Init( void )
{
    GPIO_InitType GPIO_InitStructure;

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

    /* 配置GPIO */
    GPIO_StructInit( &GPIO_InitStructure );

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

    GPIO_InitStructure.GPIO_Pins = UART1_485RE_PIN | UART1_485DE_PIN;

    GPIO_Init( UART1_485CON_PORT, &GPIO_InitStructure );
}

/**********************************************************************************************************
* brief   Uart_Init
* param
* retval
* note    配置串口的硬件参数
**********************************************************************************************************/
static void Uart_Init( void )
{
    GPIO_InitType GPIO_InitStructure;
    USART_InitType USART_InitStructure;

    #if USART1_EN
    /* 使能GPIO时钟 */
    RCC_AHBPeriphClockCmd( USART1_GPIO_RCC, ENABLE );
    /* 使能USART1时钟 */
    RCC_APB2PeriphClockCmd( RCC_APB2PERIPH_USART1, ENABLE );

    /* 配置GPIO */
    GPIO_StructInit( &GPIO_InitStructure );
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                                   /* 复用功能 */
    GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;
    /* 配置TX */
    GPIO_InitStructure.GPIO_Pins = USART1_TX_PIN;
    GPIO_InitStructure.GPIO_OutType = GPIO_OutType_PP;
    GPIO_Init( USART1_TX_PORT, &GPIO_InitStructure );
    /* 配置RX */
    GPIO_InitStructure.GPIO_Pull = GPIO_Pull_PU;
    GPIO_InitStructure.GPIO_Pins = USART1_RX_PIN ;
    GPIO_Init( USART1_RX_PORT, &GPIO_InitStructure );

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

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


    #if USE_USART_DMA_TX

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

    #endif

    #if USE_USART_DMA_RX

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

    #endif   

    #if USE_USART_RX_IDLE

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

    #else

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

    #endif
   

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

    #endif

    #if USART2_EN
    /* 使能GPIO时钟 */
    RCC_AHBPeriphClockCmd( USART2_GPIO_RCC, ENABLE );
    /* 使能USART2时钟 */
    RCC_APB1PeriphClockCmd( RCC_APB1PERIPH_USART2, ENABLE );

    /* 配置GPIO */
    GPIO_StructInit( &GPIO_InitStructure );
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; /* 复用功能 */
    GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;
    /* 配置TX */
    GPIO_InitStructure.GPIO_Pins = USART2_TX_PIN;
    GPIO_InitStructure.GPIO_OutType = GPIO_OutType_PP;
    GPIO_Init( USART2_TX_PORT, &GPIO_InitStructure );
    /* 配置RX */
    GPIO_InitStructure.GPIO_Pins = USART2_RX_PIN;
    GPIO_InitStructure.GPIO_Pull = GPIO_Pull_PU;
    GPIO_Init( USART2_RX_PORT, &GPIO_InitStructure );

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

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

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

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

    #endif
}

/**********************************************************************************************************
* brief   NVIC_Uart_Config
* param  
* retval
* note    配置串口硬件中断.
**********************************************************************************************************/
static void NVIC_Uart_Config( void )
{
    NVIC_InitType NVIC_InitStructure;

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

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

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

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

/**********************************************************************************************************
* brief  配置串口DMA
* param  
* note   
**********************************************************************************************************/
#define DATA_SIZE_DMA 32

static void uart_dma_configure( void )
{
    DMA_InitType DMA_InitStructure;
    NVIC_InitType NVIC_InitStructure;

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

    #if ( USART1_EN && USE_USART_DMA_TX )

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

    DMA_Init( UART_TX_DMA_CHANNEL, &DMA_InitStructure );

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

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

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

    #endif

    #if ( USART1_EN && USE_USART_DMA_RX )

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

    DMA_Init( UART_RX_DMA_CHANNEL, &DMA_InitStructure );

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


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

    #endif

    #if USE_DMA_IRQ

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

    #endif

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

    /* Enable DMA1 channel4 IRQ Channel */
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init( &NVIC_InitStructure );

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

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

    /* Enable USART2 DMA TX request */
    usart_dma_enable( USART2, USART_DMAReq_Tx, ENABLE );
    #endif
}

/**********************************************************************************************************
* brief   bsp_uart_init
* param
* note    串口初始化
**********************************************************************************************************/
void bsp_uart_init( void )
{
    Uart_Init();
    uart_dma_configure();
    NVIC_Uart_Config();
    Uart1_485_control_Init();

    /* RS485,DE禁止,RE使能 */
    rs485_tx_disable();
    rs485_rx_enable();
}

/**************************************************************************************************
* brief
* param
* note  
*************************************************************************************************/
void rs485_tx_disable( void )
{
    GPIO_WriteBit( UART1_485CON_PORT, UART1_485DE_PIN, Bit_RESET );
}

/**************************************************************************************************
* brief
* param
* note  
*************************************************************************************************/
void rs485_tx_enable( void )
{
    GPIO_WriteBit( UART1_485CON_PORT, UART1_485DE_PIN, Bit_SET );  /* 高电平有效,高电平允许发送 */
}

/**************************************************************************************************
* brief
* param
* note  
*************************************************************************************************/
void rs485_rx_disable( void )
{
    GPIO_WriteBit( UART1_485CON_PORT, UART1_485RE_PIN, Bit_SET );    /* 低电平有效,允许接收 */
}

/**************************************************************************************************
* brief
* param
* note  
*************************************************************************************************/
void rs485_rx_enable( void )
{
    GPIO_WriteBit( UART1_485CON_PORT, UART1_485RE_PIN, Bit_RESET );  /* 低电平有效,高电平禁止接收 */
}

/**************************************************************************************************
* brief   获取dma通道中断允许位
* param   
* note   
*************************************************************************************************/
uint8_t dma_ien_get( DMA_Channel_Type* chl, uint32_t ie_mask )
{
    return ( ( chl->CHCTRL & ie_mask ) == ie_mask );
}

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

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

    /* Enable USART2 DMA TX Channel */
    dma_channel_enable( UART_TX_DMA_CHANNEL, ENABLE );
}


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

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

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

        rs485_rx_disable();

        uart_dma_rxFull = 1;
    }

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

        uart_dma_txFull = 1;

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

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

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

        /* 通道关闭,才能对通道寄存器赋值 */
        dma_channel_enable( UART_RX_DMA_CHANNEL, DISABLE );  
        /* 接收的数据长度 */
        Uart_dma_rxbyte = DATA_SIZE_DMA - DMA_GetCurrDataCounter( UART_RX_DMA_CHANNEL );
        /* 重新赋值计数值 */
        DMA_SetCurrDataCounter( UART_RX_DMA_CHANNEL, DATA_SIZE_DMA );  
        dma_channel_enable( UART_RX_DMA_CHANNEL, ENABLE );
        
        rs485_rx_disable();
        uart_dma_rxFull = 1;
        
        TMR_SetCounter( TMR6, 0 );
    }
   
    /* 串口接收字符中断 */
    if( USART_GetITStatus( USART1, USART_INT_RDNE ) != RESET )
    {
        USART_ClearFlag( USART1, USART_FLAG_RDNE );        

        receive = USART_ReceiveData(USART1);
        Queue_In( &sQueUart, &receive );

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


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

        rs485_tx_disable();
        rs485_rx_enable();        

        TMR_SetCounter( TMR6, 0 );        
    }
}

#endif


#if USART2_EN

/*
*********************************************************************************************************
*   函 数 名: USART1_IRQHandler USART2_IRQHandler
*   功能说明: USART中断服务程序
*   形    参: 无
*   返 回 值: 无
*   note    :中断函数名定义在startup_at32f421c8t7.s
*********************************************************************************************************
*/

void USART2_IRQHandler( void )
{
    static uint16_t idx = 0;


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


    }
}

#endif





/*
*********************************************************************************************************
*   函 数 名: fputc
*   功能说明: 重定义putc函数,可以使用printf函数从串口1打印输出
*   形    参: 无
*   返 回 值: 无
*********************************************************************************************************
*/
int fputc( int ch, FILE *f )
{
    /* 发送数据 */
    USART_SendData( USART1, ( uint16_t )ch );

    /* 等待发送结束 */
    while( USART_GetFlagStatus( USART1, USART_FLAG_TRAC ) == RESET )
    {
    }

    return ch;
}

/*
*********************************************************************************************************
*   函 数 名: fgetc
*   功能说明: 重定义getc函数,可以使用getchar函数从串口1输入数据
*   形    参: 无
*   返 回 值: 无
*********************************************************************************************************
*/
int fgetc( FILE *f )
{
    /* 等待接收到数据 */
    while( USART_GetFlagStatus( USART1, USART_FLAG_RDNE ) == RESET )
    {
    }

    return ( int )USART_ReceiveData( USART1 );
}














/********************************************* End of file **********************************************/







使用特权

评论回复
地板
nos001|  楼主 | 2022-10-20 15:22 | 只看该作者
本帖最后由 nos001 于 2022-10-20 15:23 编辑
/*
*********************************************************************************************************
*
*        程序名称: 串口驱动模块
*        文件名称: bsp_usart.h
*        版    本: v1.0.0
*   编    写:
*   日    期: 2021年4月9日
*
*        Copyright (C), 2021-2031, xxxx
*
*********************************************************************************************************
*/

#ifndef __BSP_USART_H
#define __BSP_USART_H

#include <stdint.h>


/**
* 串口1的GPIO分布
* TXD:   PA9     PB6
* RXD:   PA10    PB7
*/
#define USART1_TX_PORT         GPIOB
#define USART1_TX_PIN          GPIO_Pins_6
#define USART1_TX_SOURCE       GPIO_PinsSource6
#define USART1_TX_AF           GPIO_AF_0

#define USART1_RX_PORT         GPIOB
#define USART1_RX_PIN          GPIO_Pins_7
#define USART1_RX_SOURCE       GPIO_PinsSource7
#define USART1_RX_AF           GPIO_AF_0

#define USART1_GPIO_RCC        RCC_AHBPERIPH_GPIOB

/* 串口2的GPIO分布
* TXD: PA2 PA14
* RXD: PA3 PA15 PB0
*
* 复用功能见参考手册6.3.7
*/
#define USART2_TX_PORT         GPIOA
#define USART2_TX_PIN          GPIO_Pins_2
#define USART2_TX_SOURCE       GPIO_PinsSource2
#define USART2_TX_AF           GPIO_AF_1

#define USART2_RX_PORT         GPIOA
#define USART2_RX_PIN          GPIO_Pins_3
#define USART2_RX_SOURCE       GPIO_PinsSource3
#define USART2_RX_AF           GPIO_AF_1

#define USART2_GPIO_RCC        RCC_AHBPERIPH_GPIOA

/**
* UART1 RS485 Control Pin
* RE: PC13    低电平有效
* DE: PC14    高电平有效
*/
#define UART1_485CON_PORT      GPIOC
#define UART1_485RE_PIN        GPIO_Pins_13
#define UART1_485DE_PIN        GPIO_Pins_14

#define UART1_485CON_GPIO_RCC  RCC_AHBPERIPH_GPIOC



/* 波特率 */
#define USART1_BAUD            38400
#define USART2_BAUD            115200


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



#define USART1_EN              1
#define USART2_EN              0

#define USE_USART_DMA_TX       1
#define USE_USART_DMA_RX       1
#define USE_USART_RX_IDLE      1
#define USE_DMA_IRQ            0


/* variable */
#define UART_BUF_SIZE          64


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

extern uint8_t uart_dma_txFull;
extern uint8_t uart_dma_rxFull;
extern uint8_t Uart_dma_rxbyte;
extern uint8_t uart_rx_3_5space;
extern uint8_t uart_rx_en_tmr;
extern uint8_t rs485_tx_len;
extern uint8_t *pUart_tx_buf;


/* function */
extern void bsp_uart_init(void);

extern void rs485_tx_disable(void);
extern void rs485_tx_enable(void);
extern void rs485_rx_disable(void);
extern void rs485_rx_enable(void);

extern void rs485_dma_tx( uint8_t len );


#endif

/********************************************* End of file **********************************************/







使用特权

评论回复
5
nos001|  楼主 | 2022-10-20 15:26 | 只看该作者
/*
*********************************************************************************************************
* brief : TMR6_GLOBAL_IRQHandler,
* param :
* note  : TIMR6中断, Uart接收空闲超3.5字符中断
*********************************************************************************************************
*/
#if 1

void TMR6_GLOBAL_IRQHandler(void)
{
    if (TMR_GetINTStatus(TMR6, TMR_INT_Overflow) != RESET)
    {
        /* 清零溢出中断标志 */
        TMR_ClearITPendingBit(TMR6, TMR_INT_Overflow);

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

        /* 串口接收间隔超过3.5字符 */
                if( uart_rx_en_tmr )
                {
                        uart_rx_en_tmr = 0;
            uart_rx_3_5space = 1;
                        rs485_rx_disable();        
                }
        else  /* 超过2s未产生串口接收中断,使能485接收 */
                {
                        rs485_tx_disable();
            rs485_rx_enable();               
                }                       
    }
}

/********************************************* End of file **********************************************/
#endif

使用特权

评论回复
6
nos001|  楼主 | 2022-10-20 15:27 | 只看该作者
/*
*********************************************************************************************************
*        函 数 名: bsp_timer6_init
*        功能说明: TMR6初始化
*        形    参: 无
*        返 回 值: 无
*********************************************************************************************************
*/
void bsp_timer6_init(void)
{
    TMR_TimerBaseInitType TMR_TMReBaseStructure;
    NVIC_InitType NVIC_InitStructure;

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

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

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

    TMR_Cmd( TMR6, ENABLE );


    /* 使能TMR6全局中断 */
    NVIC_InitStructure.NVIC_IRQChannel = TMR6_GLOBAL_IRQn;    /* IRQ通道 */
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; /* 抢占优先级别 */
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        /* 从优先级别 */
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           /* 使能IRQ通道 */
    NVIC_Init(&NVIC_InitStructure);
}

使用特权

评论回复
7
SILA丶BOTON| | 2023-10-28 13:37 | 只看该作者
大佬 可以上传个完整的通讯范例学习嘛  小白有几个头文件对应不上

使用特权

评论回复
8
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.part02.rar

1.65 MB

Code_AT32F421.part01.rar

10 MB

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

66

帖子

0

粉丝