遥测终端机串口,遥测终端机采集数据,遥测终端机RS485,RS485串口通讯

网站导航

开发相关(SCJ)

当前位置: 首页 > 开发相关(SCJ)
STM32H7 串口驱动(DMA 中断)
时间:2025-03-31 13:06:44 点击次数:

遥测终端机采用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--

XML地图

QQ在线咨询
销售(微信同号)
18571629282
技术(微信同号)
13871206075