遥测终端机采用STM32H743作为主控,使用的外部接口比如RS485,RS232均为串口,STM32H743 拥有8个串口,可以通过中断方式进行串口数据收发,也可以通过DMA方式进行串口收发,但是通过DMA方式的时候,需要注意串口数据一致性问题,如果开启了DMA,通过DMA接收的数据,然后用DMA发送出去是没问题的,但是通过CPU去访问会发现内存并没有数据(数据没有同步到内存,还在cache中),解决办法只能是的强制刷新DCache。
通过DMA接收的数据,必须调用 SCB_CleanInvalidateDCache(); //强制刷新DCACHE,开启了DMA接收后,数据不会被同步到内存,直接用CPU访问就会出问题
遥测终端机(STM32H743)串口驱动代码如下:
/*************************************************************************************************************
* 文件名 : uart.c
* 功能 : STM32H7 UART驱动
* 作者 : www.scj-water.com
* 创建时间 : 2025-03-23
* 最后修改时间 : 2025-03-23
* 详细: 2025-03-30:增加DMA收发,中断接收状态下FIFO模式貌似没用,无法通过FIFO阈值进行触发接收中断
* 遇到奇怪的问题,如果用DMA接收数据,用DMA发送没问题,但是CPU访问发现接收数据并没更新,怀疑是Cache导致,用中断时就没有这个问题。
*************************************************************************************************************/
#include "system.h"
#include "uart.h"
//说明:串口1,串口6为APB2,84MHZ,串口2-5 APB1,48MHZ
//串口1:PB14(TXD),PB15(RXD)/PA9(TXD),PA10(RXD)/PB6(TXD),PB7(RXD)/
//串口2:PA2(TXD),PA3(RXD)/PD5(TXD),PD6(RXD)
//串口3:PB10(TXD),PB11(RXD)/PC10(TXD),PC11(RXD)/PD8(TXD),PD9(RXD)
//串口4:PA0(TXD),PA1(RXD)/PC10(TXD),PC11(RXD)/PA12(TXD),PA11(RXD)/PH14(TXD),PH14(RXD)/PI9(RXD)/PD1(TXD),PD0(RXD)/PB9(TXD),PB8(RXD)
//串口5:PC12(TXD),PD2(RXD)/PB13(TXD),PB12(RXD)/PB6(TXD),PB5(RXD)
//串口6:PC6(TXD),PC7(RXD)/PG14(TXD),PG9(RXD)
//串口7:PF7(TXD),PF6(RXD)/PE8(TXD),PE7(RXD)/PA14(TXD),PA8(RXD)/PB4(TXD),PB3(RXD)
//串口8:PJ8(TXD),PJ9(RXD)/PE1(TXD),PE0(RXD)
static NVIC_IRQ_TYEP const g_UartIrqBuff[] = {IRQ_USART1, IRQ_USART2, IRQ_USART3, IRQ_UART4, IRQ_UART5, IRQ_USART6, IRQ_UART7, IRQ_UART8}; //中断定义
static SYS_DEV_CLOCK const g_UartDevClockBuff[] = { DEV_USART1, DEV_USART2, DEV_USART3, DEV_UART4, DEV_UART5, DEV_USART6, DEV_UART7, DEV_UART8}; //串口时钟使能定义
static SYS_DEV_RST const g_UartDevResetBuff[] = { DEV_RST_USART1, DEV_RST_USART2, DEV_RST_USART3, DEV_RST_UART4, DEV_RST_UART5, DEV_RST_USART6, DEV_RST_UART7, DEV_RST_UART8 }; //串口外设复位定义
//UART外设结构指针
static const USART_TypeDef * const USARTxN[8] = {USART1,USART2,USART3,UART4,UART5,USART6,UART7,UART8};
//DAM通道设置
#if UART_DMA_EN
#include "dma.h"
static const DMAxSx_CH_TYPE scg_UART_RxDMAChannel[8] = { DMA2_CH0, DMA2_CH1, DMA2_CH2, DMA2_CH3, DMA2_CH4, DMA2_CH5, DMA2_CH6, DMA2_CH7}; //接收通道=接收通道一直占用,发送通道临时获取
static const DMA12_MUX1_REQ scg_UART_Tx_DMA_Trigger[8] = { DMA12_USART1_TX, DMA12_USART2_TX, DMA12_USART3_TX, DMA12_UART4_TX,
DMA12_UART5_TX, DMA12_USART6_TX, DMA12_UART7_TX, DMA12_UART8_TX }; //UART发送DMA触发源
static const DMA12_MUX1_REQ scg_UART_Rx_DMA_Trigger[8] = { DMA12_USART1_RX, DMA12_USART2_RX, DMA12_USART3_RX, DMA12_UART4_RX,
DMA12_UART5_RX, DMA12_USART6_RX, DMA12_UART7_RX, DMA12_UART8_RX }; //UART接收DMA触发源
#endif //UART_DMA_EN
#define UARTx_ClearRxError(UARTx) {UARTx->ICR = BIT3 | BIT2 | BIT1 | BIT0;} //清除接收错误状态寄存器
//串口初始化错误说明
static const char *pLastUartError[8];
//相关UART状态结构
typedef struct
{
FlagStatus NewDataFlag;//接收到新数据
FlagStatus BuffFull; //接收Buff满
FlagStatus IntRx; //是否开启中断接收
u8 *RxBuff; //接收Buff指针
u16 RxBuffSize; //接收缓冲区大小,一帧数据大小
u16 UartRxCnt; //接收数据计数器
} UartRx_TypeDef;
//UARTx 接收状态结构
static UartRx_TypeDef sg_UartRx[8];
/*************************************************************************************************************************
* 函数 : bool UARTx_Init(UART_CH_Type ch,u32 Speed,bool RX_Int)
* 功能 : 串口初始化
* 参数 : ch:通道选择,0->usart1,Speed:串口速度,RX_Int:是否时能中断接受
* 返回 : TRUE:成功,FALSE:失败
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 20120403
* 最后修改时间 : 2013-11-17
* 说明 : USART1~UART5,对应通道UART_CH1-UART_CH5
2013-11-17:添加DMA支持
*************************************************************************************************************************/
bool UARTx_Init(UART_CH_Type ch,u32 Speed,bool RX_Int)
{
USART_TypeDef *UARTx = (USART_TypeDef *)USARTxN[ch]; //获取对应通道硬件基址指针
NVIC_IRQ_TYEP irq_n;
bool isStatus = TRUE;
if(ch > UART_ChMax - 1)
return FALSE; //端口号超出范围
irq_n = g_UartIrqBuff[ch]; //获取中断号
SYS_DeviceClockEnable(g_UartDevClockBuff[ch], TRUE); //使能串口时钟
SYS_DeviceReset(g_UartDevResetBuff[ch]); //STM32H7外设复位
UARTx->CR2 = 0; //设置清零
//设置波特率分频系数
UARTx_SetBaudRate(ch, Speed); //设置波特率
//配置UART
UARTx->CR1 &= ~BIT0; //关闭UART
UARTx->CR1 |= 1<<29 ; //使能FIFO,1个开始位,8位数据
UARTx->CR1 |= 1<<3; //TE:发送器使能 (Transmitter enable)
UARTx->CR1 |= 1<<2; //RE:接收器使能 (Receiver enable)
UARTx->CR3 = (4<<29)|(4<<25)|BIT12; //关闭DMA,关闭流控,发送FIFO 7/8 接收FIFO 3/4阈值,禁止上溢出功能
#if UART_DMA_EN
UARTx->CR3 |= BIT7; //DMA发送模式使能
//DMA_MemoryToPeripheralConfig(USARTxChannel[ch], (u32)&UARTx->TDR, DMA_SIZE_8BIT, TRUE, FALSE); //DMA存储器到外设传输配置
#else
#endif //UART_DMA_EN
UARTx_SetRxBuff(ch,NULL,0); //设置串口接收缓冲区
UARTx_ClearRxInt(ch); //清除串口接收中断标志
if(RX_Int)
{
#if UART_DMA_EN
UARTx->CR3 |= BIT6; //DMA接收模式使能
#else
UARTx->CR1 |= BIT5; //RXNEIE = 1,开RXNE中断,即开启接收中断
UARTx->CR3 |= BIT28; //接收FIFO达到阈值中断有效,没有达到阈值就需要依赖超时,可以积累多个字节一次性触发中断,提高效率
UARTx->CR1 |= BIT31; //接收FIFO满中断使能
SYS_NVIC_IntEnable(irq_n, TRUE); //开启串口接收中断
sg_UartRx[ch].IntRx = SET; //中断接收标志有效
#endif //UART_DMA_EN
}
else
{
SYS_NVIC_IntEnable(irq_n, FALSE); //关闭串口接收中断
sg_UartRx[ch].IntRx = RESET; //中断接收标志无效
}
UARTx->CR1 |= BIT0; //使能UART
UARTx->RQR = BIT3 | BIT4; //清除接收发送FIFO
UARTx->ICR = BIT3 | BIT2 | BIT1 | BIT0; //清除错误标志
return isStatus; //初始化成功,返回0
}
/*************************************************************************************************************************
* 函数 : void UARTx_PowerDown(UART_CH_Type ch)
* 功能 : UART掉电
* 参数 : ch:通道选择
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 20120403
* 最后修改时间 : 20130316
* 说明 : 进入低功耗模式
*************************************************************************************************************************/
void UARTx_PowerDown(UART_CH_Type ch)
{
USART_TypeDef *UARTx = (USART_TypeDef *)USARTxN[ch]; //获取对应通道硬件基址指针
if(ch > UART_ChMax - 1) //判断端口是否超出范围
return;
if(!(UARTx->ISR & 0x80)) //等待发送完成才开启低功耗模式
{
Delay_MS(10); //如果串口没有发送完成,等待10ms
}
UARTx->CR1 &= ~(1 << 0); //UE位写0,开启低功耗-关闭串口
}
/*************************************************************************************************************************
* 函数 : void UARTx_PowerUp(UART_CH_Type ch)
* 功能 : UART上电
* 参数 : ch:通道选择
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 20120403
* 最后修改时间 : 20130316
* 说明 : 退出低功耗模式
*************************************************************************************************************************/
void UARTx_PowerUp(UART_CH_Type ch)
{
if(ch > UART_ChMax - 1) //判断端口是否超出范围
return;
((USART_TypeDef *)USARTxN[ch])->CR1 |= (1 << 0); //UE位1,退出低功耗模式
}
/*************************************************************************************************************************
* 函数 : void UARTx_SetBaudRate(UART_CH_Type ch,u32 baud)
* 功能 : 串口波特率设置
* 参数 : ch:通道选择,baud:波特率,如9600,115200等等
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2013316
* 最后修改时间 : 2013316
* 说明 : USART1~UART5,对应通道UART_CH1-UART_CH5
会自动关闭串口,同时恢复到之前状态
*************************************************************************************************************************/
void UARTx_SetBaudRate(UART_CH_Type ch,u32 baud)
{
u32 SysClk = 0;
USART_TypeDef *UARTx = (USART_TypeDef *)USARTxN[ch]; //获取对应通道硬件基址指针
u32 CR1;
switch(ch)
{
case UART_CH1: //UART1
case UART_CH6: //UART6
{
SysClk = SYS_GetPCLK2Speed(); //获取系统时钟
}break;
default:
{
SysClk = SYS_GetPCLK1Speed(); //获取系统时钟
}break;
}
CR1 = UARTx->CR1;
UARTx->CR1 &= ~BIT0; //关闭串口
SysClk = (SysClk + baud / 2) / baud; //计算波特率分频系数-同时进行四舍五入
UARTx->BRR = SysClk; //设置波特率整数部分 OVER8=0 16倍过采样
UARTx->CR1 = CR1; //恢复配置
}
/*************************************************************************************************************************
* 函数 : bool UARTx_Config(UART_CH_Type ch,UART_Config_TypeDef * cfg)
* 功能 : 串口配置
* 参数 : ch:通道选择,0->usart1;cfg:串口配置结构指针
* 返回 : TRUE:成功,FALSE:失败
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 20120408
* 最后修改时间 : 20120408
* 说明 : USART1~UART5,对应通道UART_CH1-UART_CH5
*************************************************************************************************************************/
bool UARTx_Config(UART_CH_Type ch,UART_Config_TypeDef * cfg)
{
USART_TypeDef *UARTx = (USART_TypeDef *)USARTxN[ch]; //获取对应通道硬件基址指针
if(ch > UART_ChMax - 1) //判断端口是否超出范围
return FALSE;
UARTx_PowerDown(ch); //进入掉电模式,进行配置
switch (cfg->OddEvenVerify) //设置校验位
{
case UART_VERIFY_NULL: //无校验
{
UARTx->CR1 &= ~BIT28;
UARTx->CR1 &= ~BIT12; //一个起始位,8个数据位
UARTx->CR1 &= ~BIT10; //禁止校验控制
}break;
case UART_ODD: //奇校验
{
UARTx->CR1 &= ~BIT28;
UARTx->CR1 |= BIT12; //一个起始位,9个数据位
UARTx->CR1 |= BIT10; //使能校验控制
UARTx->CR1 |= BIT9; //奇校验
}break;
case UART_EVEN: //偶校验
{
UARTx->CR1 &= ~BIT28;
UARTx->CR1 |= BIT12; //一个起始位,9个数据位
UARTx->CR1 |= BIT10; //使能校验控制
UARTx->CR1 &= ~BIT9; //偶校验
}break;
default :
{
UARTx_PowerUp(ch); //串口重新上电
return FALSE; //设置错误,返回校验设置错误
}
}
if(cfg->StopBitWidth == UART_STOP_1BIT) //设置停止位
{
UARTx->CR2 &= ~(0x3 << 12); //清除设置,默认一个停止位
}
else if(cfg->StopBitWidth == UART_STOP_2BIT)
{
UARTx->CR2 &= ~(0x3 << 12);
UARTx->CR2 |= (0x2 << 12); //2个停止位
}
else
{
UARTx_PowerUp(ch); //串口重新上电
return FALSE; //停止位设置错误,返回错误2
}
UARTx_PowerUp(ch); //串口重新上电
return TRUE; //设置完成,返回TRUE
}
/*************************************************************************************************************************
* 函数 : void UARTx_EnableRx(UART_CH_Type ch,bool Enable)
* 功能 : 串口接收使能
* 参数 : ch:通道选择,ENABLE:使能接收,DISABLE:关闭接收
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2013316
* 最后修改时间 : 2013316
* 说明 : USART1~UART5,对应通道UART_CH1-UART_CH5
*************************************************************************************************************************/
void UARTx_EnableRx(UART_CH_Type ch,bool Enable)
{
USART_TypeDef *UARTx = (USART_TypeDef *)USARTxN[ch]; //获取对应通道硬件基址指针
if(Enable)
UARTx->CR1 |= 0x04; //RE = 1;接收使能
else
UARTx->CR1 &= ~0x04; //RE = 0;接收关闭
}
/*************************************************************************************************************************
* 函数 : void UARTx_SendByte(UART_CH_Type ch,u8 data)
* 功能 : UART单字节发送
* 参数 : ch:通道号,dataL:要发送的数据
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 20120403
* 最后修改时间 : 2012-04-29
* 说明 : USART1~UART5,对应通道UART_CH1-UART_CH5
将等待发送完成注释掉了,可以稍微提高发送速度
2017-07-02:取消单字节发送的DMA模式,一个字节发送使用DMA效率相反会降低,防止发送失败阻塞串口
*************************************************************************************************************************/
void UARTx_SendByte(UART_CH_Type ch,u8 data)
{
u32 retry = 0xffffff;
USART_TypeDef *UARTx = (USART_TypeDef *)USARTxN[ch]; //获取对应通道硬件基址指针
if(ch > UART_ChMax - 1) //判断端口是否超出范围
return;
while(((UARTx->ISR & BIT7)==0) && retry) //等待数据发送完成 或 发送FIFO没有满
{
retry --;
nop;
}
UARTx->TDR = data; //发送数据
#if !UART_TX_TO_FIFI
retry = 0xfffff;
while((!(UARTx->ISR & (1<<6))) && retry) //等待TC = 1;也就是发送完成,数据从发送FIFO发送出去了才认为发送已经完成
{
retry --;
}
UARTx->ISR &= ~(1 << 6); //清除发送完成标志
#endif //!UART_TX_TO_FIFI
}
/*************************************************************************************************************************
* 函数 : void UARTx_SendData(UART_CH_Type ch,u8 *tx_buff,u16 byte_number)
* 功能 : UART数据发送函数
* 参数 : ch:通道号,tx_buff:发送缓冲区,byte_number:需要发送的字节
* 返回 : 无
* 依赖 : void UART_SendByte(u8 ch,u8 data)
* 作者 : www.scj-water.com
* 时间 : 20120403
* 最后修改时间 : 20120403
* 说明 : 非DMA方式,非FIFO方式发送
*************************************************************************************************************************/
void UARTx_SendData(UART_CH_Type ch,u8 *pTxBuff,u16 DataLen)
{
#if(UART_DMA_EN) //使能DMA发送
int dma_ch;
USART_TypeDef* UARTx;
bool isNotDMA = FALSE;
u32 retry = 0xffffff;
u32 i;
#endif
if(ch > UART_ChMax - 1) //判断端口是否超出范围
return;
#if UART_DMA_EN
UARTx = (USART_TypeDef*)USARTxN[ch]; //获取对应通道硬件基址指针
if (DataLen < UART_DMA_TX_MIN) //不需要用DMA发送
{
isNotDMA = TRUE;
}
else //DMA发送
{
dma_ch = DMA_GetIdleChannel();//获取一个DMA空闲通道(用完后需要释放)
//uart_printf("dma_ch=%d\r\n", dma_ch);
if (dma_ch < 0) //DMA获取失败,直接用字节发送
{
isNotDMA = TRUE;
}
else //申请成功-使用DMA发送
{
while (((UARTx->ISR & BIT7) == 0) && retry) //等待数据发送完成 或 发送FIFO没有满
{
retry--;
nop;
}
UARTx->ICR = BIT6; //清除TCCF 发送完成标志
DMA_MemoryToPeripheralConfig((DMAxSx_CH_TYPE)dma_ch, scg_UART_Tx_DMA_Trigger[ch], (u32)&UARTx->TDR, DMA_SIZE_8BIT, TRUE, FALSE);//DMA存储器到外设传输配置
DMA_StartTrans((DMAxSx_CH_TYPE)dma_ch, (u32)pTxBuff, NULL, DataLen); //开始传输
if (DMA_WaitComplete((DMAxSx_CH_TYPE)dma_ch, (u32)DataLen * 2) == FALSE) //等待存储器DMA传输完成
{
u16 cnt = DMA_GetCompleteResidualCnt((DMAxSx_CH_TYPE)dma_ch);
ERROR_S("dma_ch=%d 发送超时 cnt=%d\r\n", dma_ch, cnt);
DMA_StopTrans((DMAxSx_CH_TYPE)dma_ch); //传输超时,强制关闭 DMA 传输
}
DMA_ReleaseChannel(dma_ch);//释放当前使用的通道
}
}
if (isNotDMA) //不需要用DMA发送
{
for (i = 0; i < DataLen; i++) //循环发送,直至发送完毕
{
UARTx_SendByte(ch, pTxBuff[i]);
}
}
#else
{
u16 i;
for(i = 0;i < DataLen;i++) //循环发送,直至发送完毕
{
UARTx_SendByte(ch,pTxBuff[i]);
}
}
#endif //UART_DMA_EN
#if (!UART_TX_TO_FIFI) //要求等待数据发送完成
UARTx_WaitSendComplete(ch); //等待数据发送完成-从串口发送完成
#endif //UART_TX_TO_FIFI
}
/*************************************************************************************************************************
* 函数 : void UARTx_SendString(UART_CH_Type ch,char *pString)
* 功能 : UART发送字符串
* 参数 : ch:通道号
pString:字符串指针
* 返回 : 无
* 依赖 : void UART_SendByte(u8 ch,u8 data)
* 作者 : www.scj-water.com
* 时间 : 2013-04-18
* 最后修改时间 : 2013-04-18
* 说明 : 非DMA方式,非FIFO方式发送
*************************************************************************************************************************/
#include "string.h"
void UARTx_SendString(UART_CH_Type ch,char *pString)
{
if(ch > UART_ChMax - 1) //判断端口是否超出范围
return;
/*while(*pString != '\0')
{
UARTx_SendByte(ch, *pString ++);
}*/
UARTx_SendData(ch, (u8 *)pString, strlen(pString));
}
/*************************************************************************************************************************
* 函数 : bool UARTx_GetNewDataFlag(UART_CH_Type ch)
* 功能 : 获取串口新数据标志
* 参数 : ch:通道选择
* 返回 : TRUE:成功,FALSE:失败
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 20120403
* 最后修改时间 : 20120403
* 说明 : 用于判断是否有新的数据,会清除掉新数据标志的
*************************************************************************************************************************/
bool UARTx_GetNewDataFlag(UART_CH_Type ch)
{
if(ch > UART_ChMax - 1) //判断端口是否超出范围
return FALSE;
if(sg_UartRx[ch].IntRx == SET) //开启了中断接收
{
if(sg_UartRx[ch].NewDataFlag == SET) //有新数据
{
sg_UartRx[ch].NewDataFlag = RESET; //清除标志
return TRUE; //返回有新数据
}
}
else //没开启中断接收
{
if(((USART_TypeDef *)USARTxN[ch])->ISR & BIT5) //RXNE=1,接收到新数据
{
((USART_TypeDef *)USARTxN[ch])->ISR &= ~BIT5; //清除标志
return TRUE;
}
}
return FALSE;
}
/*************************************************************************************************************************
* 函数 : bool UARTx_GetRxBuffFullFlag(UART_CH_Type ch)
* 功能 : 获取串口接收缓冲区满标志
* 参数 : ch:通道选择
* 返回 : TRUE:成功,FALSE:失败
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 20120403
* 最后修改时间 : 20120403
* 说明 : 用于判断接收缓冲区是否满,会清除标志
*************************************************************************************************************************/
bool UARTx_GetRxBuffFullFlag(UART_CH_Type ch)
{
if(ch > UART_ChMax - 1) //判断端口是否超出范围
return FALSE;
#if UART_DMA_EN
if(DMA_GetCompleteResidualCnt(scg_UART_RxDMAChannel[ch]) == 0)
{
if(((USART_TypeDef *)USARTxN[ch])->ISR & BIT3) //清除过载标志
{
((USART_TypeDef*)USARTxN[ch])->RQR = BIT3; //清除接收FIFO
}
return TRUE;
}
else
return FALSE;
#else
if(sg_UartRx[ch].BuffFull == SET) //缓冲区已满
{
sg_UartRx[ch].BuffFull = RESET; //清除满标志
return TRUE;
}
return FALSE;
#endif //UART_DMA_EN
}
/*************************************************************************************************************************
* 函数 : void UART_ClearRxInt(UART_CH_Type ch)
* 功能 : 清除串口接收中断标志
* 参数 : ch:通道选择
* 返回 : 物
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 20120403
* 最后修改时间 : 20120403
* 说明 : 用于清除接收标志
*************************************************************************************************************************/
void UARTx_ClearRxInt(UART_CH_Type ch)
{
if(ch > UART_ChMax - 1) //判断端口是否超出范围
return;
((USART_TypeDef *)USARTxN[ch])->ICR = BIT11 | BIT3 | BIT2 | BIT1| BIT0; //清除标志
}
/*************************************************************************************************************************
* 函数 : u8 UARTx_GetNewData(UART_CH_Type ch)
* 功能 : 获取串口新数据
* 参数 : ch:通道选择
* 返回 : 收到的数据
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 20120403
* 最后修改时间 : 20120403
* 说明 : 用于接收一个字节数据
*************************************************************************************************************************/
u8 UARTx_GetNewData(UART_CH_Type ch)
{
if(ch > UART_ChMax - 1) //判断端口是否超出范围
return 0;
return (((USART_TypeDef *)USARTxN[ch])->RDR); //返回数据
}
/*************************************************************************************************************************
* 函数 : void UARTx_SetRxBuff(UART_CH_Type ch,u8 *RxBuff,u16 RxBuffSize)
* 功能 : 设置串口接收缓冲区
* 参数 : ch:通道选择,RxBuffSize:缓冲区大小,RxBuff:缓冲区指针
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 20120403
* 最后修改时间 : 20120403
* 说明 : 一定要设置,否则开启中断接收时可能会异常
2018-04-19 增加清除过载标志,否则在上电初始化的时候不停的接收数据可能会导致串口不能触发DMA接收数据
*************************************************************************************************************************/
void UARTx_SetRxBuff(UART_CH_Type ch,u8 *RxBuff,u16 RxBuffSize)
{
#ifdef _UCOS_II_
OS_CPU_SR cpu_sr;
#endif //_UCOS_II_
if(ch > UART_ChMax - 1) //判断端口是否超出范围
return;
#if UART_DMA_EN
DMA_PeripheralToMemoryConfig(scg_UART_RxDMAChannel[ch], scg_UART_Rx_DMA_Trigger[ch], (u32)RxBuff, (u32)&((USART_TypeDef *)USARTxN[ch])->RDR, DMA_SIZE_8BIT, RxBuffSize, TRUE); //DMA外设到存储器传输配置
DMA_StartTrans(scg_UART_RxDMAChannel[ch], (u32)RxBuff, NULL, RxBuffSize);
#endif //UART_DMA_EN
#ifdef _UCOS_II_
OS_ENTER_CRITICAL();
#endif //_UCOS_II_
sg_UartRx[ch].RxBuffSize = RxBuffSize; //设置缓冲区大小
sg_UartRx[ch].RxBuff = RxBuff; //设置缓冲区指针
#if !UART_DMA_EN
sg_UartRx[ch].UartRxCnt = 0; //计数器清零
#endif //!UART_DMA_EN
#ifdef _UCOS_II_
OS_EXIT_CRITICAL();
#endif //_UCOS_II_
//2018-04-19 增加清除过载标志,否则在上电初始化的时候不停的接收数据可能会导致串口不能触发DMA接收数据
if(((USART_TypeDef *)USARTxN[ch])->ISR & BIT3) //清除过载标志
{
((USART_TypeDef*)USARTxN[ch])->RQR = BIT3; //清除接收FIFO
}
UARTx_ClearRxError(((USART_TypeDef*)USARTxN[ch])); //清除接收错误状态寄存器
}
/*************************************************************************************************************************
* 函数 : u32 UARTx_GetRxCnt(UART_CH_Type ch)
* 功能 : 获取串口接收数据计数器
* 参数 : ch:通道选择
* 返回 : 接收到的数据数量
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 20130307
* 最后修改时间 : 20130307
* 说明 : 2025-03-15 清除错误中断
*************************************************************************************************************************/
u32 UARTx_GetRxCnt(UART_CH_Type ch)
{
if(ch > UART_ChMax - 1) //判断端口是否超出范围
return 0;
if (((USART_TypeDef*)USARTxN[ch])->ISR & BIT3) //清除过载标志
{
((USART_TypeDef*)USARTxN[ch])->RQR = BIT3; //清除接收FIFO
}
UARTx_ClearRxError(((USART_TypeDef*)USARTxN[ch])); //清除接收错误状态寄存器
#if UART_DMA_EN
return sg_UartRx[ch].RxBuffSize - DMA_GetCompleteResidualCnt(scg_UART_RxDMAChannel[ch]);
#else
return sg_UartRx[ch].UartRxCnt; //返回计数值
#endif //UART_DMA_EN
}
/*************************************************************************************************************************
* 函数 : void UARTx_ClearRxCnt(UART_CH_Type ch)
* 功能 : 清除串口接收数据计数器
* 参数 : ch:通道选择
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 20130307
* 最后修改时间 : 20130307
* 说明 : 无
*************************************************************************************************************************/
void UARTx_ClearRxCnt(UART_CH_Type ch)
{
#if UART_DMA_EN
USART_TypeDef* UARTx;
#endif //UART_DMA_EN
if(ch > UART_ChMax - 1) //判断端口是否超出范围
return;
#if UART_DMA_EN
UARTx = (USART_TypeDef*)USARTxN[ch]; //获取对应通道硬件基址指针
DMA_StartTrans(scg_UART_RxDMAChannel[ch], (u32)sg_UartRx[ch].RxBuff, NULL, sg_UartRx[ch].RxBuffSize);
#else
sg_UartRx[ch].UartRxCnt = 0; //计数器清零
#endif //UART_DMA_EN
UARTx_ClearRxInt(ch); //清除串口接收中断标志-主要是错误标志
}
/*************************************************************************************************************************
* 函数 : const char *UARTx_GetLastError(UART_CH_Type ch)
* 功能 : 获取串口初始化错误信息
* 参数 : ch:通道选择
* 返回 : 字符串
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2017-11-26
* 最后修改时间 : 2017-11-26
* 说明 :
*************************************************************************************************************************/
const char *UARTx_GetLastError(UART_CH_Type ch)
{
if(ch > UART_ChMax - 1) return NULL;
return pLastUartError[ch];
}
#if !UART_DMA_EN
/*************************************************************************************************************************
* 函数 : __inline void USARTx_IRQHandler (UART_CH_Type ch)
* 功能 : UARTx中断接收函数
* 参数 : ch:串口号;
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2025-03-15
* 最后修改时间 : 2025-03-15
* 说明 : 无
*************************************************************************************************************************/
__INLINE void USARTx_IRQHandler (UART_CH_Type ch)
{
static vu8 temp;
while (((USART_TypeDef*)USARTxN[ch])->ISR & BIT5) //接收FIFO中有数据
{
if (sg_UartRx[ch].UartRxCnt < sg_UartRx[ch].RxBuffSize) //接收缓冲区有剩余
{
(sg_UartRx[ch].RxBuff)[(sg_UartRx[ch].UartRxCnt)++] = USARTxN[ch]->RDR; //将数据存放到缓冲区
if (sg_UartRx[ch].UartRxCnt == sg_UartRx[ch].RxBuffSize) //缓冲区已满
{
//sg_UartRx[ch].UartRxCnt = 0; //接收计数器清零 2019-09-12 接收满了之后不再接收了
sg_UartRx[ch].BuffFull = SET; //缓冲区已满标志
}
}
else //数据满了,需要读取一次
{
//temp = USARTxN[ch]->RDR;
//temp = temp;
((USART_TypeDef*)USARTxN[ch])->RQR = BIT3; //清除接收FIFO
}
}
sg_UartRx[ch].NewDataFlag = SET; //收到新数据标志
UARTx_ClearRxInt(ch); //清除串口接收中断标志
}
//UART1中断接收函数
void USART1_IRQHandler (void) {USARTx_IRQHandler(UART_CH1);}
//UART2中断接收函数
void USART2_IRQHandler (void) {USARTx_IRQHandler(UART_CH2);}
//UART3中断接收函数
void USART3_IRQHandler (void) {USARTx_IRQHandler(UART_CH3);}
//UART4中断接收函数
void UART4_IRQHandler(void) {USARTx_IRQHandler(UART_CH4);}
//UART5中断接收函数
void UART5_IRQHandler(void) {USARTx_IRQHandler(UART_CH5);}
//UART6中断接收函数
void USART6_IRQHandler(void) {USARTx_IRQHandler(UART_CH6);}
//UART7中断接收函数
void UART7_IRQHandler (void) {USARTx_IRQHandler(UART_CH7);}
//UART8中断接收函数
void UART8_IRQHandler (void) {USARTx_IRQHandler(UART_CH8);}
#endif //UART_DMA_EN
#undef UART_ChMax
//UART.H
/*************************************************************************************************************
* 文件名 : uart.h
* 功能 : STM32H7 UART驱动
* 作者 : www.scj-water.com
* 创建时间 : 2025-03-23
* 最后修改时间 : 2025-03-23
* 详细:
*************************************************************************************************************/
#ifndef _UART_H
#define _UART_H
#include "system.h"
#include "stdio.h"
/***********************配置相关************************/
#define UART_DMA_EN 1 //1:使能DAM收发模式;0:关闭DMA收发模式
#define UART_TX_TO_FIFI 1 //1:数据发送到发送FIFO则认为发送完成;0:数据从FIFO发送完成则认为发送完成
#define UART_ChMax 8 //串口通道数量
#define UART_DMA_TX_MIN 7 //使用DMA发送最少的数据
/*********************************************************/
//UART配置相关结构定义
typedef struct
{
u8 OddEvenVerify; //奇偶校验,奇,偶,无
u8 StopBitWidth; //停止位位宽1,2
} UART_Config_TypeDef;
//奇偶校验
#define UART_VERIFY_NULL 0 //无校验
#define UART_ODD 1 //奇校验
#define UART_EVEN 2 //偶校验
//停止位
#define UART_STOP_1BIT 0 //一个停止位
#define UART_STOP_2BIT 1 //2个停止位
//串口选择,串口1开始,到串口8
typedef enum
{
UART_CH1 = 0, //UART1
UART_CH2 = 1, //UART2
UART_CH3 = 2, //UART3
UART_CH4 = 3, //UART4
UART_CH5 = 4, //UART5
UART_CH6 = 5, //UART6
UART_CH7 = 6, //UART7
UART_CH8 = 7, //UART8
}UART_CH_Type;
//相关API
bool UARTx_Init(UART_CH_Type ch,u32 Speed,bool RX_Int); //串口初始化
bool UARTx_Config(UART_CH_Type ch,UART_Config_TypeDef * cfg); //串口配置
void UARTx_SendByte(UART_CH_Type ch,u8 data); //UART单字节发送
void UARTx_SendData(UART_CH_Type ch,u8 *tx_buff,u16 byte_number); //UART数据发送函数
void UARTx_SendString(UART_CH_Type ch,char *pString); //UART发送字符串
void UARTx_PowerDown(UART_CH_Type ch); //UART掉电
void UARTx_PowerUp(UART_CH_Type ch); //UART上电
bool UARTx_GetNewDataFlag(UART_CH_Type ch); //获取串口新数据标志
bool UARTx_GetRxBuffFullFlag(UART_CH_Type ch); //获取串口接收缓冲区满标志
u8 UARTx_GetNewData(UART_CH_Type ch); //获取串口新数据
void UARTx_SetRxBuff(UART_CH_Type ch,u8 *RxBuff,u16 RxBuffSize); //设置串口接收缓冲区
void UARTx_ClearRxInt(UART_CH_Type ch); //清除串口接收中断标志
u32 UARTx_GetRxCnt(UART_CH_Type ch); //获取串口接收数据计数器
void UARTx_ClearRxCnt(UART_CH_Type ch); //清除串口接收数据计数器
void UARTx_EnableRx(UART_CH_Type ch,bool Enable); //串口接收使能
void UARTx_SetBaudRate(UART_CH_Type ch,u32 baud); //计算并设置串口波特率
const char* UARTx_GetLastError(UART_CH_Type ch); //获取串口初始化错误信息
#endif //UART
//测试代码如下
#define SYS_COMM_BUFF_SIZE 2048
u8 SysCommBuff[SYS_COMM_BUFF_SIZE];
void uart_test(void)
{
u16 cnt1,cnt2;
u16 i;
SYS_DeviceClockEnable(DEV_GPIOA, TRUE);
SYS_GPIOx_OneInit(GPIOA, 10, IN_IPU, IN_NONE); //RX 上拉输入
SYS_GPIOx_SetAF(GPIOA, 9, AF7_USART1);
SYS_GPIOx_SetAF(GPIOA, 10, AF7_USART1);
UARTx_Init(UART_CH1, 115200, TRUE);
UARTx_SetRxBuff(UART_PRINTF_CH, SysCommBuff, SYS_COMM_BUFF_SIZE-1); //初始化调试串口接收缓冲区
while(1)
{
/*if (USART1->ISR & BIT5) //有数据
{
UARTx_SendByte(UART_PRINTF_CH, USART1->RDR);
}*/
SYS_DelayMS(100);
cnt1 = UARTx_GetRxCnt(UART_PRINTF_CH);
SYS_DelayMS(10);
cnt2 = UARTx_GetRxCnt(UART_PRINTF_CH);
if((cnt1 == cnt2) && (cnt1 != 0)) //收到数据
{
SCB_CleanInvalidateDCache(); //强制刷新DCACHE,开启了DMA接收后,数据不会被同步到内存,直接用CPU访问就会出问题
UARTx_SendData(UART_PRINTF_CH, SysCommBuff, cnt1); //发送数据
UARTx_ClearRxCnt(UART_PRINTF_CH);
}
}
}
遥测终端机采集数据常用的通讯接口都是RS485,底层使用的串口通讯代码就是uart,传感器也通常都是使用RS485通讯,一样是使用的芯片的UART接口。
上一篇:没有了!
如果您有任何问题,请跟我们联系!
欢迎来电:18571629282
Copyright © 2024 武汉水测家科技有限公司 版权所有 鄂ICP备2022002065号-1 --WTRExpert English website--