mcu与威纶HMI的modbus rtu rs485通信
本帖最后由 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; // 位寄存器区
uint16_t RTU_D_buf; // 字寄存器区
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 =
{
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 =
{
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
};
/**************************************************************************************************
* brief16位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 ;
uchCRCLo = auchCRCLo ;
}
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_ti = 0;
uint8_trece_byte = 8;
while( Queue_Out( &sQueUart, &UartR_buf ) )
{
rece_byte++;
}
#endif
if( Uart_dma_rxbyte < 8 )return; /* 有效报文的字节数量最少是8个字节 */
if( UartR_buf == 0x01 ) /* 站号,本站0x01 */
{
/* 接收到的CRC校验码,低字节在前,高字节在后 */
crc_val = ( UartR_buf << 8 ) | UartR_buf;
/* 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, 0x08 );
}
}
}
/**************************************************************************************************
* brief : 01功能码,
* param :
* note: 位读取,HMI的地址类型0x
**************************************************************************************************/
void modbus_rtu_01H( void )
{
/* 协议
主机发送:
rbuf01 从机地址
rbuf01 功能码
rbufxx 寄存器起始地址高字节
rbufxx 寄存器起始地址低字节
rbufxx 寄存器数量高字节
rbufxx 寄存器数量低字节
rbufxx CRC校验低字节
rbufxx CRC校验高字节
从机应答:
tbuf01 从机地址
tbuf01 功能码
tbufxx 返回字节数
tbufxx 数据1
tbufxx 数据2
tbufxx 数据3
tbufxx 数据4
tbufxx 数据5
tbufxx CRC校验高字节
tbuf xx CRC校验低字节
*/
uint16_t i, j;
uint16_t bit_base;
uint16_t bit_addr;
uint16_t bit_len;
uint8_tbit_baseH, bit_baseL;
uint8_tbit_lenH, bit_lenL;
uint8_tbit_Val;
uint16_t crc_val;
if( UartR_buf == 0x01 )
{
bit_baseH = UartR_buf; /* 位首址高字节 */
bit_baseL = UartR_buf; /* 位首址低字节 */
bit_lenH= UartR_buf; /* 位长高字节 */
bit_lenL= UartR_buf; /* 位长低字节 */
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 = UartR_buf; /* 站号 */
UartT_buf = UartR_buf; /* 功能码 */
UartT_buf = ( bit_len / 8 ) + ( ( bit_len % 8 ) != 0 ); /* 要返回的字节数, 不能整除8的时候要多返回一个字节 */
for( i = 0; i < UartT_buf; 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 + 3 ); /* CRC校验 */
UartT_buf ] = ( uint8_t )( crc_val ); /* CRC低字节 */
UartT_buf ] = ( uint8_t )( crc_val >> 8 ); /* CRC高字节 */
rs485_dma_tx( UartT_buf + 5 );
}
}
/**************************************************************************************************
* brief05功能码
* param
* note 写单个位状态,HMI的地址类型0x
**************************************************************************************************/
void modbus_rtu_05H( void )
{
uint16_t crc_val, bit_addr;
if( UartR_buf == 0x05 )
{
bit_addr = UartR_buf << 8 | UartR_buf; /* 位地址 */
if( UartR_buf == 0xff ) /* 强制ON */
{
RTU_M_buf |= 0x0001 << ( bit_addr % 16 );
}
else /* 强制OFF */
{
RTU_M_buf &= ~( 0x0001 << ( bit_addr % 16 ) );
}
UartT_buf = UartR_buf; /* 站号 */
UartT_buf = UartR_buf; /* 功能码 */
UartT_buf = UartR_buf; /* 起始地址高字节 */
UartT_buf = UartR_buf; /* 起始地址低字节 */
UartT_buf = UartR_buf; /* 返回状态高字节 */
UartT_buf = UartR_buf; /* 返回状态低字节 */
crc_val = CRC16_calc( UartT_buf, 6 ); /* 校验 */
UartT_buf = ( uint8_t )( crc_val ); /* 校验低位 */
UartT_buf = ( uint8_t )( crc_val >> 8 ); /* 校验高位 */
rs485_dma_tx( 8 ); /* 发送返回 */
}
}
/**************************************************************************************************
* brief03功能码
* 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 == 0x03 )
{
reg_base= ( UartR_buf << 8 ) | UartR_buf; /* 寄存器地址 */
len_words = ( UartR_buf << 8 ) | UartR_buf; /* 读取的字长 */
len_byte= len_words * 2; /* 字节数 */
UartT_buf = UartR_buf; /* 站号 */
UartT_buf = UartR_buf; /* 功能码 */
UartT_buf = ( uint8_t )( len_words * 2 ); /* 字节数 */
for( i = 0; i < len_words; i++ ) /* 发送相应字节 */
{
UartT_buf = ( uint8_t )( RTU_D_buf >> 8 ); /* 数据高字节 */
UartT_buf = ( uint8_t )( RTU_D_buf ); /* 数据低字节 */
}
len_byte = idx;
crc_val = CRC16_calc( UartT_buf, len_byte ); /* 校验 */
UartT_buf = ( uint8_t )( crc_val ); /* 校验低位 */
UartT_buf = ( uint8_t )( crc_val >> 8 ); /* 校验高位 */
len_byte = idx;
rs485_dma_tx( len_byte ); /* 返回 */
}
}
/**************************************************************************************************
* brief06功能码
* param
* note 写单个寄存器,HMI的地址类型6x
**************************************************************************************************/
void modbus_rtu_06H( void )
{
uint16_t reg_addr, reg_val, crc_val;
if( UartR_buf == 0x06 ) /* 功能码 */
{
reg_addr = ( UartR_buf << 8 ) | UartR_buf; /* 写入地址 */
reg_val= ( UartR_buf << 8 ) | UartR_buf; /* 寄存器值 */
RTU_D_buf = reg_val; /* 存入字寄存器区 */
UartT_buf = UartR_buf; /* 站号 */
UartT_buf = UartR_buf; /* 功能码 */
UartT_buf = UartR_buf; /* 寄存器地址高字节 */
UartT_buf = UartR_buf; /* 寄存器地址低字节 */
UartT_buf = UartR_buf; /* 数据高字节 */
UartT_buf = UartR_buf; /* 数据低字节 */
crc_val = CRC16_calc( UartT_buf, 6 ); /* 校验 */
UartT_buf = ( uint8_t )( crc_val ); /* 校验低位 */
UartT_buf = ( uint8_t )( crc_val >> 8 ); /* 校验高位 */
rs485_dma_tx( 8 ); /* 返回 */
}
}
/**************************************************************************************************
* brief10H功能码
* param
* note 写多个寄存器, HMI的地址类型4x
**************************************************************************************************/
void modbus_rtu_10H( void )
{
uint16_t reg_base;
uint16_t reg_num;
uint16_t crc_val, i;
uint8_tw_bytes;
if( UartR_buf == 0x10 )
{
reg_base = ( UartR_buf << 8 ) + UartR_buf; /* 要写入的起始地址 */
reg_num= ( UartR_buf << 8 ) + UartR_buf; /* 寄存器数量 */
w_bytes = UartR_buf; /* 要写的字节数量 */
for( i = 0; i < ( w_bytes / 2 ); i++ ) /* 将值写入寄存器 */
{
RTU_D_buf = ( UartR_buf<< 8 ) + UartR_buf;
}
UartT_buf = UartR_buf; /* 站号 */
UartT_buf = UartR_buf; /* 功能码 */
UartT_buf = UartR_buf; /* 返回地址高位 */
UartT_buf = UartR_buf; /* 返回地址低位 */
UartT_buf = UartR_buf;
UartT_buf = UartR_buf;
crc_val = CRC16_calc( UartT_buf, 6 ); /* CRC校验 */
UartT_buf = ( uint8_t )( crc_val ); /* CRC低字节 */
UartT_buf = ( 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 = 0x01; /* 返回站号 */
switch( type )
{
case 0x08:
UartT_buf = 0x80 + func; /* 返回错误功能码 */
UartT_buf = 0x08; /* 返回错误代码,08:CRC校验错误 */
break;
case 0x01:
UartT_buf = 0x80 + func; /* 返回错误功能码 */
UartT_buf = 0x01; /* 返回错误代码,01:功能码错误 */
break;
case 0x02:
UartT_buf = 0x80 + func; /* 返回错误功能码 */
UartT_buf = 0x02; /* 返回错误代码,02:地址错误 */
break;
case 0x03:
UartT_buf = 0x80 + func; /* 返回错误功能码 */
UartT_buf = 0x03; /* 返回错误代码,03:数据错误 */
break;
case 0x04:
UartT_buf = 0x80 + func; /* 返回错误功能码 */
UartT_buf = 0x04; /* 返回错误代码,04:不支持的功能码 */
break;
}
crc_val = CRC16_calc( UartT_buf, 3 ); /* CRC校验 */
UartT_buf = (uint8_t)(crc_val); /* 校验低字节 */
UartT_buf = (uint8_t)(crc_val>>8); /* 校验高字节 */
rs485_dma_tx(5);
}
#endif
/**
**************************************************************************
* 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; // 位寄存器区
extern uint16_t RTU_D_buf; // 字寄存器区
extern void modbus_rtu_analyze( void );
#endif
本帖最后由 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; // 发送数据数组
uint8_t UartR_buf; // 接收数据数组
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;
/**********************************************************************************************************
* 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 );
}
/**************************************************************************************************
* briefrs485 dma 发送
* paramlen发送字节数
* 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
/**********************************************************************************************************
* briefDMA1_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;
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 );
}
}
/**********************************************************************************************************
* briefUSART1中断服务程序
* 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: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_RCCRCC_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; // 发送数据数组
extern uint8_t UartR_buf; // 接收数据数组
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 **********************************************/
/*
*********************************************************************************************************
* 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 /*
*********************************************************************************************************
* 函 数 名: 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);
} 大佬 可以上传个完整的通讯范例学习嘛小白有几个头文件对应不上 SILA丶BOTON 发表于 2023-10-28 13:37
大佬 可以上传个完整的通讯范例学习嘛小白有几个头文件对应不上
完整的工程,水平风格都不好,将就看吧。
获取、写HIMI的位和字寄存器函数:
Get_RTU_D
Get_RTU_M
Wrt_RTU_D
Set_RTU_M
页:
[1]