打印
[综合信息]

HC32F4裸机UART串口驱动(寄存器操作)

[复制链接]
454|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
qcliu|  楼主 | 2023-10-17 17:29 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
HC32F4裸机UART串口驱动,接口与之前使用STM32接口兼容,主要使用DMA操作,可以选择中断接收还是DMA接收模式,如果有嵌入式实时操作系统,优先使用DMA,简单方便,由于DMA通道不够多,每个串口接收独占,发送循环调用,具体见下一篇博文的DMA代码。

/*************************************************************************************************************
* 文件名:                        uart.c
* 功能:                        HC32F46X UART通讯支持
* 作者:                        cp1300@139.com
* 创建时间:                2021-06-13
* 最后修改时间:        2021-06-13
* 详细:                        2021-09-12:修复设置串口传输位数bug,正常要设置CR1 BIT12位为0
*                                        2021-12-23:增加 void UARTx_EnableRx(UART_CH_Type ch, bool Enable) 串口接收使能
                                        2021-12-24:清除接收错误状态寄存器 ORE FE PE ,这3个错误会导致接收停止,在485通讯情况下,可能会出现异常的接收情况
                                                                比如产生了接收数据帧错误,导致后续无法接收。
                                        2022-03-14 增加波特率时钟预分频设置,防止无法产生低速率时钟,无法产生低速率时钟的时候,串口IO口会输出低电平,并且无法正常工作                       
*************************************************************************************************************/
#include "hc32f46x_system.h"
#include "uart.h"
#include "typedef.h"

//串口中断接收函数
#if !UART_DMA_EN
__inline static void UARTx_IRQHandler(UART_CH_Type ch);                                //串口中断处理
static void UART1_RX_IRQHandler(void) {UARTx_IRQHandler(UART_CH1);}        //串口1接收中断服务程序
static void UART2_RX_IRQHandler(void) {UARTx_IRQHandler(UART_CH2);}        //串口2接收中断服务程序
static void UART3_RX_IRQHandler(void) {UARTx_IRQHandler(UART_CH3);}        //串口3接收中断服务程序
static void UART4_RX_IRQHandler(void) {UARTx_IRQHandler(UART_CH4);}        //串口4接收中断服务程序


//中断函数集合       
static const void *scg_pUartIrqHandle[UART_ChMax] =
{(const void *)UART1_RX_IRQHandler,         (const void *)UART2_RX_IRQHandler,         (const void *)UART3_RX_IRQHandler,  (const void *)UART4_RX_IRQHandler};

//串口中断所占用的中断线
static const IRQn_Type scg_UartIrqType[UART_ChMax] =
{SYS_IRQ_UART1_RX_NUM,         SYS_IRQ_UART2_RX_NUM,         SYS_IRQ_UART3_RX_NUM,         SYS_IRQ_UART4_RX_NUM};       
       
//串口中断源
static const INT_SOURCE_TYPE scg_UartIntSourceType[UART_ChMax] =
{SYS_INT_USART1_RI,         SYS_INT_USART2_RI,         SYS_INT_USART3_RI,         SYS_INT_USART4_RI};               
#endif //UART_DMA_EN

//时钟使能       
static const SYS_DEV_CLOCK scg_UARTx_DeviceClockEnable[UART_ChMax]         =
{DEV_USART1,         DEV_USART2,         DEV_USART3,         DEV_USART4};       
//串口基址       
static const u32 scg_UARTx_Base[UART_ChMax] = {UART1_BASE, UART2_BASE, UART3_BASE, UART4_BASE};       

//DAM通道设置
#if UART_DMA_EN
#include "dma.h"
static const DMAx_CH_TYPE scg_UART_RxDMAChannel[4] = {DMA2_CH1, DMA2_CH2, DMA2_CH3,DMA2_CH4};        //接收通道
static const u32 scg_UART_RX_DR_ADDR[4] = {UART1_BASE + 0x04 + 2, UART2_BASE + 0x04 + 2, UART3_BASE + 0x04 + 2, UART4_BASE + 0x04 + 2};        //UART接收寄存器地址
static const u32 scg_UART_TX_DR_ADDR[4] = {UART1_BASE + 0x04 + 0, UART2_BASE + 0x04 + 0, UART3_BASE + 0x04 + 0, UART4_BASE + 0x04 + 0};        //UART发送寄存器地址
static const INT_SOURCE_TYPE scg_UART_Rx_DMA_Trigger[4] = {SYS_INT_USART1_RI, SYS_INT_USART2_RI, SYS_INT_USART3_RI, SYS_INT_USART4_RI};        //UART接收DMA触发源
static const INT_SOURCE_TYPE scg_UART_Tx_DMA_Trigger[4] = {SYS_INT_USART1_TI, SYS_INT_USART2_TI, SYS_INT_USART3_TI, SYS_INT_USART4_TI};        //UART发送DMA触发源
#endif        //UART_DMA_EN



//相关UART状态结构
typedef struct
{
        bool                isNewDataFlag;        //接收到新数据
        bool                isBuffFull;                //接收Buff满
        bool                isIntRx;                //是否开启中断接收
        u8                         *RxBuff;                //接收Buff指针
        u16                        RxBuffSize;                //接收缓冲区大小,一帧数据大小
        u16                 UartRxCnt;                //接收数据计数器
        u8                        TempData;                //用于接收溢出后读取数据寄存器,清除读取数据标志
} UartRx_TypeDef;
static UartRx_TypeDef sg_UartRx[UART_ChMax];


#define UARTx_ClearRxError()                {UARTx->CR1 |= (BIT19|BIT17|BIT16);}        //2021-12-24 清除接收错误状态寄存器 ORE FE PE ,这3个错误会导致接收停止

/*************************************************************************************************************************
*函数                :        bool UARTx_Config(UART_CH_Type ch,UART_Config_TypeDef * cfg)
*功能                :        串口配置
*参数                :        ch:串口号;cfg:配置结构体
*返回                :        TRUE:配置成功; FALSE: 配置失败
*依赖                        :         底层宏定义
*作者               :        cp1300@139.com
*时间                     :        2021-06-13
*最后修改时间        :        2021-06-13
*说明                :        配置前最好确保数据已经发送完成,没有数据正在发送
*************************************************************************************************************************/
bool UARTx_Config(UART_CH_Type ch,UART_Config_TypeDef * cfg)
{
        u32 temp;
        UART_TypeDef *UARTx;
       
        if(ch > (UART_ChMax - 1)) return FALSE;                                                        //端口号超出范围
        UARTx = (UART_TypeDef *) scg_UARTx_Base[ch];                                        //获取设备基址
       
        temp = UARTx->CR1;
        //奇偶校验
        switch(cfg->OddEvenVerify)       
        {
                case UART_ODD:        //奇校验
                {
                        temp |= BIT10;        //开启校验
                        temp |= BIT9;        //奇校验
                }break;
                case UART_EVEN:        //偶校验
                {
                        temp |= BIT10;        //开启校验
                        temp &= ~BIT9;        //偶校验
                }break;
                default://无校验
                {
                        temp &= ~BIT10;        //关闭校验
                }break;       
        }
        //数据长度
        if(cfg->DataBitWidth == UART_DATA_8BIT)        //数据位长度设置8bit
        {
                temp &= ~BIT12;
        }
        else
        {
                temp |= BIT12;
        }
        UARTx->CR1 = temp;
       
        //??λ
        temp = UARTx->CR2;
        if(cfg->StopBitWidth != UART_STOP_1BIT)        //不止1个停止位
        {
                temp |= BIT13;        //2个停止位
        }
        else
        {
                temp &= ~BIT13;        //1个停止位
        }
        UARTx->CR2 = temp;

        return TRUE;
}



/*************************************************************************************************************************
* 函数                        :        void UARTx_SetBaudRate(UART_CH_Type ch,u32 baud)
* 功能                        :        串口波特率设置
* 参数                        :        ch:通道选择,baud:波特率,如9600,115200等等
* 返回                        :        无
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2021-06-13
* 最后修改时间         :         2021-06-13
* 说明                        :         注意 DIV_Integer[7:0]只能 在 TE=0&RE=0(发送 /接收 禁止 )时设定
                                        DIV_Fraction[6:0]只能 在 TE=0&RE=0(发送 /接收 禁止 )时设定, 而且仅在 FBME=1时设定值 有效
                                        2022-03-14 增加波特率时钟预分频设置,防止无法产生低速率时钟,无法产生低速率时钟的时候,串口IO口会输出低电平,并且无法正常工作
*************************************************************************************************************************/
bool UARTx_SetBaudRate(UART_CH_Type ch,u32 baud)
{
        u8 Integer;
        u8 Fraction;
        UART_TypeDef *UARTx;
        float ftemp;
        u32 temp;        //PCLK频率
        u32 PR;                //预分频,防止时钟速度太高,导致无法产生第速率波特率
       
        if(ch > (UART_ChMax - 1)) return FALSE;                        //端口号超出范围
        UARTx = (UART_TypeDef *) scg_UARTx_Base[ch];        //获取设备基址
        temp = SYS_GetPCLK1Speed();                                                //获取PCLK1时钟速度
       
        //2022-03-14 增加波特率时钟预分频设置,防止无法产生低速率时钟,无法产生低速率时钟的时候,串口IO口会输出低电平,并且无法正常工作
        PR = temp /(8*baud);        //得到分频值
        if(PR >= 256*16)
        {
                temp /= 64;                        //进行64分频
                PR = 3;
        }
        else if(PR >= 256*8)
        {
                temp /= 16;                        //进行16分频
                PR = 2;
        }
        else if(PR >= 256*1)
        {
                temp /= 4;                        //进行4分频
                PR = 1;
        }
        else
        {
                PR = 0;                                //不分频
        }

        ftemp = (double)temp/(8*baud);
        if(ch ==UART_CH2)
        {
                uart_printf("uart ftemp=%f\r\n",ftemp);
        }
        if(ftemp < 1 || ftemp >= 256) return FALSE;                //时钟范围错误
        Integer = ftemp;        //整数分频
       
       
       
        //计算实际的波特率,由于整数分频存在误差,最终波特率只会偏大,不会偏小
        temp /= 8;
        temp /= Integer;
        //计算需要的波特率与实际波特率比值
        ftemp = baud;        //用目标波特率除以实际波特率,得到偏差比值,这个比值只会小于1,并且会接近1
        ftemp /= temp;        //得到了比值乘以256 - 128 就是最终的小数补偿值
        ftemp *= 256.0f;
        ftemp -= 128.0f;
       
        Integer -= 1;                //实际整数分频-1
        //小数分频计算
        if(ftemp < 1)
        {
                ftemp = 0;
                UARTx->CR1 &= ~BIT29;        //FBME=0关闭小数波特率功能
        }
        else
        {
                UARTx->CR1 |= BIT29;        //FBME=1使能小数波特率功能
        }
        Fraction = ftemp;                        //计算得到最终的小数分频
       
        temp = Integer;
        temp <<= 8;
        temp |= Fraction&0x7F;
       
        UARTx->PR = PR;                                //预分频寄存器为0
        UARTx->BRR = temp;
        if(ch ==UART_CH2)
        {
                uart_printf("uart temp=%d\r\n",temp);
        }
        return TRUE;
}


/*************************************************************************************************************************
* 函数                        :        bool UARTx_Init(UART_CH_Type ch,u32 Speed,bool isEnableRx)
* 功能                        :        串口初始化
* 参数                        :        ch:通道选择,0->usart1,Speed:串口速度,isEnableRx:是否使能接收
* 返回                        :        TRUE:成功,FALSE:失败
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2021-06-13
* 最后修改时间         :         2021-06-23
* 说明                        :        
*************************************************************************************************************************/
bool UARTx_Init(UART_CH_Type ch,u32 Speed,bool isEnableRx)
{
        UART_Config_TypeDef cfg;
        UART_TypeDef *UARTx;

        if(ch > (UART_ChMax - 1)) return FALSE;                                                        //端口号超出范围
        UARTx = (UART_TypeDef *) scg_UARTx_Base[ch];                                        //获取设备基址
       
        SYS_DeviceClockEnable(scg_UARTx_DeviceClockEnable[ch], TRUE);                //外设时钟使能或关闭控制

        //UARTx->CR1 = 0xFFFFFFF3ul;
   // UARTx->CR1 = 0x80000000ul;
       
        UARTx->CR1 = 0;                                                                                                        //控制器寄存器复位
        UARTx->CR2 = 0;
        UARTx->CR3 = 0;
       
        UARTx->CR1 |= BIT31;        //检测方式为RX下降沿
        //UARTx->CR1 |= BIT30;        //使能数字滤波
        UARTx->CR1 |= BIT20|BIT19|BIT17|BIT16;        //清除各种标志
        UARTx->CR1 |= BIT15;        //过采样为8位

        //配置
        cfg.DataBitWidth = UART_DATA_8BIT;                                                                //数据宽度8
        cfg.OddEvenVerify = UART_VERIFY_NULL;                                                        //无奇偶校验
        cfg.StopBitWidth = UART_STOP_1BIT;                                                                //1个停止位
        if(UARTx_SetBaudRate(ch, Speed) == FALSE) return FALSE;                        //设置波特率
        if(UARTx_Config(ch, &cfg) == FALSE) return FALSE;                                //设置串口数据格式
        sg_UartRx[ch].isIntRx = FALSE;                                                                        //没有开启中断接收

        //IO初始化
       
        //使能了接收
        if(isEnableRx)
        {
#if(UART_DMA_EN)
                UARTx->CR1 |= BIT2;        //接收使能
#else //中断方式               
                UARTx->CR1 |= BIT5;        //接收中断使能
                INTC_RegisterIRQHandler(scg_UartIrqType[ch], scg_UartIntSourceType[ch], (void (*)(void))scg_pUartIrqHandle[ch]);//注册中断服务程序
                NVIC_ClearPendingIRQ(scg_UartIrqType[ch]);
                NVIC_SetPriority(scg_UartIrqType[ch], UART_INT_PRIO);
                NVIC_EnableIRQ(scg_UartIrqType[ch]);
               
                UARTx->CR1 |= BIT2;        //接收使能
#endif //UART_DMA_EN
               
                sg_UartRx[ch].isIntRx = TRUE;                                                                                        //开启了接收
        }
        //发送使能
        UARTx->CR1 |= BIT3;

        return TRUE;
}


/*************************************************************************************************************************
* 函数                        :        void UARTx_EnableRx(UART_CH_Type ch,bool Enable)
* 功能                        :        串口接收使能
* 参数                        :        ch:通道选择,TRUE:使能接收,FALSE:关闭接收
* 返回                        :        无
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2021-12-23
* 最后修改时间                :        2021-12-23
* 说明                        :        
*************************************************************************************************************************/
void UARTx_EnableRx(UART_CH_Type ch, bool Enable)
{
        UART_TypeDef* UARTx;

        if (ch > (UART_ChMax - 1)) return;                                                //端口号超出范围
        UARTx = (UART_TypeDef*)scg_UARTx_Base[ch];                                //获取设备基址

        UARTx_ClearRxError();                                                                        //2021-12-24 清除接收错误状态寄存器 ORE FE PE ,这3个错误会导致接收停止
        if (Enable)
                UARTx->CR1 |= BIT2;                                                                        //RE = 1;接收使能
        else
                UARTx->CR1 &= ~BIT2;                                                                //RE = 0;接收关闭
}


/*************************************************************************************************************************
* 函数                        :        void UARTx_SendByte(UART_CH_Type ch,u8 data)
* 功能                        :        UART单字节发送
* 参数                        :        ch:通道号,dataL:要发送的数据
* 返回                        :        无
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2021-06-13
* 最后修改时间         :         2021-06-13
* 说明                        :         单字节发送不要使用DMA,浪费
                                        2021-07-28:解决不能while空循环,V6编译器会优化掉问题,后面加个nop即可;
*************************************************************************************************************************/
void UARTx_SendByte(UART_CH_Type ch,u8 data)
{
        UART_TypeDef *UARTx;
       
        if(ch > (UART_ChMax - 1)) return;                                                //端口号超出范围
        UARTx = (UART_TypeDef *) scg_UARTx_Base[ch];                        //获取设备基址
        while((UARTx->SR & BIT7)==0) nop;                                                //等待TX发送寄存器为空
        UARTx->DR = data;                                                                                //发送数据-写到带发送寄存器,并不会等待数据发送完成
}




/*************************************************************************************************************************
* 函数                        :        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)
* 作者                        :        cp1300@139.com
* 时间                        :        2021-06-13
* 最后修改时间         :         2021-06-13
* 说明                        :        
*************************************************************************************************************************/
void UARTx_SendData(UART_CH_Type ch,u8 *pTxBuff,u16 DataLen)
{
        u16 i;
#if(UART_DMA_EN) //使能DMA发送       
        int dma_ch;
        UART_TypeDef *UARTx;
        bool isNotDMA = FALSE;
#endif
       
        if(ch > (UART_ChMax - 1)) return;                                                //端口号超出范围
       
#if(UART_DMA_EN) //使能DMA发送
        UARTx = (UART_TypeDef *) scg_UARTx_Base[ch];                        //获取设备基址

       
        dma_ch = DMA_GetIdleChannel();//获取一个DMA空闲通道(用完后需要释放)
        //uart_printf("dma_ch=%d\r\n", dma_ch);
        if(dma_ch < 0)        //DMA获取失败,直接用字节发送
        {
                isNotDMA = TRUE;
        }
        else //DMA发送
        {
                if(DataLen < UART_DMA_TX_MIN)
                {
                        isNotDMA = TRUE;
                }
                else //DMA发送
                {
                        while((UARTx->SR & BIT7)==0);                                                        //等待TX发送寄存器为空
                        DMA_MemoryToPeripheralConfig((DMAx_CH_TYPE) dma_ch, (u32)&pTxBuff[1], scg_UART_TX_DR_ADDR[ch], DMA_SIZE_8BIT,  DataLen-1, scg_UART_Tx_DMA_Trigger[ch], TRUE);//DMA存储器到外设传输配置
                        UARTx_SendByte(ch, pTxBuff[0]);                                                        //初始化DMA后不会被触发发送,只有先发送一条数据,让发送寄存器空从无效变为有效,之后就会触发DMA发送
                        if(DMA_WaitMemComplete((DMAx_CH_TYPE) dma_ch, (u32)DataLen*2) == FALSE)                //等待存储器DMA传输完成
                        {
                                u16 cnt = DMA_GetCompleteResidualCnt((DMAx_CH_TYPE) dma_ch);
                                DEBUG("dma_ch=%d 发送超时 cnt=%d\r\n",dma_ch, cnt);
                                DMA_StopTrans((DMAx_CH_TYPE) dma_ch);                                                                //传输超时,强制关闭 DMA 传输
                        }
                        DMA_ReleaseChannel(dma_ch);//释放当前使用的通道
                }       
        }
       
        if(isNotDMA)        //不需要用DMA发送
        {
                for(i = 0;i < DataLen;i++)                                                        //循环发送,直至发送完毕
                {
                        UARTx_SendByte(ch, pTxBuff);
                }
        }
       
       
#else        
       
        for(i = 0;i < DataLen;i++)                                                        //循环发送,直至发送完毕
        {
                 UARTx_SendByte(ch, pTxBuff);
        }

#endif //UART_DMA_EN

#if (!UART_TX_TO_FIFI)        //要求等待数据发送完成
        UARTx_WaitSendComplete(ch);                                //等待数据发送完成-从串口发送完成
#endif //UART_TX_TO_FIFI               
}


/*************************************************************************************************************************
* 函数                        :        void UARTx_WaitSendComplete(UART_CH_Type ch, u32 TimeOutMs)
* 功能                        :        等待数据发送完成-从串口发送完成
* 参数                        :        ch:通道号;TimeOutMs:超时时间,单位ms,0:一直等待
* 返回                        :        无
* 依赖                        :        无
* 作者                        :        cp1300@139.com
* 时间                        :        2021-06-13
* 最后修改时间         :         2021-06-13
* 说明                        :        2021-01-13
*************************************************************************************************************************/
void UARTx_WaitSendComplete(UART_CH_Type ch, u32 TimeOutMs)
{
        UART_TypeDef *UARTx;
       
        if(ch > (UART_ChMax - 1)) return;                                                //端口号超出范围
        UARTx = (UART_TypeDef *) scg_UARTx_Base[ch];                        //获取设备基址
        if(TimeOutMs == 0) TimeOutMs = U32_MAX;
        while(((UARTx->SR & BIT6) == 0) && TimeOutMs)                        //等待发送完成
        {
                SYS_DelayMS(1);
                TimeOutMs --;
        }
}


/*************************************************************************************************************************
* 函数                        :        void UARTx_SendString(UART_CH_Type ch,char *pString)
* 功能                        :        UART发送字符串
* 参数                        :        ch:通道号
                                        pString:字符串指针
* 返回                        :        无
* 依赖                        :        void UART_SendByte(u8 ch,u8 data)
* 作者                        :        cp1300@139.com
* 时间                        :        2021-06-13
* 最后修改时间         :         2021-06-13
* 说明                        :        
*************************************************************************************************************************/
#include "string.h"
void UARTx_SendString(UART_CH_Type ch,char *pString)
{       
        if(ch > (UART_ChMax - 1)) return;                                        //端口号超出范围
       
        UARTx_SendData(ch, (u8 *)pString, strlen(pString));
}


/*************************************************************************************************************************
* 函数                        :        bool UARTx_GetNewDataFlag(UART_CH_Type ch)
* 功能                        :        获取串口新数据标志
* 参数                        :        ch:通道选择
* 返回                        :        TRUE:成功,FALSE:失败
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2021-06-13
* 最后修改时间         :         2021-06-13
* 说明                        :         用于判断是否有新的数据,会清除掉新数据标志的
*************************************************************************************************************************/
bool UARTx_GetNewDataFlag(UART_CH_Type ch)
{
        UART_TypeDef *UARTx;
       
        if(ch > (UART_ChMax - 1)) return FALSE;                                                //端口号超出范围
        UARTx = (UART_TypeDef *) scg_UARTx_Base[ch];                                //获取设备基址

        if(sg_UartRx[ch].isIntRx == TRUE)                                                        //开启了中断接收
        {
                if(sg_UartRx[ch].isNewDataFlag == TRUE)                                 //有新数据
                {
                         sg_UartRx[ch].isNewDataFlag = FALSE;                                //清除标志
                        return TRUE;                                                                                //返回有新数据
                }
        }
        else                                                                                                                //没开启中断接收
        {
                 if((UARTx->SR & BIT5) == 0)                                                        //接收FIFO不为空
                {
                        return TRUE;
                }
        }
        return FALSE;
}


/*************************************************************************************************************************
* 函数                        :        bool UARTx_GetRxBuffFullFlag(UART_CH_Type ch)
* 功能                        :        获取串口接收缓冲区满标志
* 参数                        :        ch:通道选择
* 返回                        :        TRUE:成功,FALSE:失败
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2021-06-13
* 最后修改时间         :         2021-06-13
* 说明                        :         用于判断接收缓冲区是否满,会清除标志
*************************************************************************************************************************/
bool UARTx_GetRxBuffFullFlag(UART_CH_Type ch)
{
        if(ch > (UART_ChMax - 1)) return FALSE;                                        //端口号超出范围

        if(sg_UartRx[ch].isBuffFull == TRUE)                        //缓冲区已满
        {
                 sg_UartRx[ch].isBuffFull = FALSE;                        //清除满标志
                return TRUE;
        }
        return FALSE;
}


/*************************************************************************************************************************
* 函数                        :        u8 UARTx_GetNewData(UART_CH_Type ch)
* 功能                        :        获取串口新数据
* 参数                        :        ch:通道选择
* 返回                        :        收到的数据
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2021-06-13
* 最后修改时间         :         2021-06-13
* 说明                        :         用于接收一个字节数据
*************************************************************************************************************************/
u8 UARTx_GetNewData(UART_CH_Type ch)
{
        UART_TypeDef *UARTx;
       
        if(ch > (UART_ChMax - 1)) return FALSE;                                                //端口号超出范围
        UARTx = (UART_TypeDef *) scg_UARTx_Base[ch];                                //获取设备基址

        return UARTx->DR >> 16;                                                                                //返回数据
}


/*************************************************************************************************************************
* 函数                        :        void UARTx_SetRxBuff(UART_CH_Type ch,u8 *RxBuff,u16 RxBuffSize)
* 功能                        :        设置串口接收缓冲区
* 参数                        :        ch:通道选择,RxBuffSize:缓冲区大小,RxBuff:缓冲区指针
* 返回                        :        无
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2021-06-13
* 最后修改时间         :         2021-06-13
* 说明                        :         一定要设置,否则开启中断接收时可能会异常
*************************************************************************************************************************/
void UARTx_SetRxBuff(UART_CH_Type ch,u8 *RxBuff,u16 RxBuffSize)
{
        #ifdef _UCOS_II_
                OS_CPU_SR  cpu_sr;
        #endif        //_UCOS_II_
               
        UART_TypeDef *UARTx;
       
        if(ch > (UART_ChMax - 1)) return;                                                //端口号超出范围
        UARTx = (UART_TypeDef *) scg_UARTx_Base[ch];                                //获取设备基址
               
        UARTx_ClearRxError();                                                        //2021-12-24 清除接收错误状态寄存器 ORE FE PE ,这3个错误会导致接收停止       
        #if UART_DMA_EN
                DMA_PeripheralToMemoryConfig(scg_UART_RxDMAChannel[ch], (u32)RxBuff, scg_UART_RX_DR_ADDR[ch], DMA_SIZE_8BIT, RxBuffSize, scg_UART_Rx_DMA_Trigger[ch], TRUE);        //DMA外设到存储器传输配置
        #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_       
}





/*************************************************************************************************************************
* 函数                        :        u32 UARTx_GetRxCnt(UART_CH_Type ch)
* 功能                        :        获取串口接收数据计数器
* 参数                        :        ch:通道选择
* 返回                        :        接收到的数据数量
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2021-06-13
* 最后修改时间         :         2021-06-13
* 说明                        :         无
*************************************************************************************************************************/
u32 UARTx_GetRxCnt(UART_CH_Type ch)
{       
        UART_TypeDef *UARTx;
        if(ch > (UART_ChMax - 1)) return 0;                                        //端口号超出范围
       
        UARTx = (UART_TypeDef *) scg_UARTx_Base[ch];                //获取设备基址
        UARTx_ClearRxError();                                                                //2021-12-24 清除接收错误状态寄存器 ORE FE PE ,这3个错误会导致接收停止
#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:通道选择
* 返回                        :        无
* 依赖                        :        底层宏定义
* 作者                        :        cp1300@139.com
* 时间                        :        2021-06-13
* 最后修改时间         :         2021-06-13
* 说明                        :         无
*************************************************************************************************************************/
void UARTx_ClearRxCnt(UART_CH_Type ch)
{
#if UART_DMA_EN       
        UART_TypeDef *UARTx;
#endif //UART_DMA_EN
       
        if(ch > (UART_ChMax - 1)) return;                                                        //端口号超出范围
       
#if UART_DMA_EN
        UARTx = (UART_TypeDef *) scg_UARTx_Base[ch];                                //获取设备基址
        DMA_StartTrans(scg_UART_RxDMAChannel[ch],  scg_UART_RX_DR_ADDR[ch], (u32)sg_UartRx[ch].RxBuff, sg_UartRx[ch].RxBuffSize);       
        UARTx_ClearRxError();                                                                                //2021-12-24 清除接收错误状态寄存器 ORE FE PE ,这3个错误会导致接收停止
        {
                u8 temp = UARTx->DR>>16;                                        //取出读到的数据
                temp = temp;
        }
       
#else
        sg_UartRx[ch].UartRxCnt = 0;                                //计数器清零
#endif //UART_DMA_EN
}


#if !UART_DMA_EN
//用于串口中断中读取数据
__inline static void UARTx_ReadRxData(UART_CH_Type ch, UART_TypeDef *UARTx)
{
    //while(UARTx->SR & BIT5)                                                                                                                                                                 //接收FIFO中有数据,循环读取
    {
        if((sg_UartRx[ch].RxBuffSize) > 0 && (sg_UartRx[ch].UartRxCnt < sg_UartRx[ch].RxBuffSize))                        //接收缓冲区大于0,并且没有满
        {
            (sg_UartRx[ch].RxBuff)[(sg_UartRx[ch].UartRxCnt) ++] = UARTx->DR>>16;                                                         //将数据存放到缓冲区
            if(sg_UartRx[ch].UartRxCnt == sg_UartRx[ch].RxBuffSize)                                                                                 //缓冲区已满
            {
                 //sg_UartRx[ch].UartRxCnt = 0;                                                                                                                                //接收计数器清零
                  sg_UartRx[ch].isBuffFull = TRUE;                                                                                                                        //缓冲区已满标志
            }       
                       
        }
        else //缓冲区满了,清除接收到的数据
        {
            sg_UartRx[ch].TempData = UARTx->DR>>16;
        }
    }       
        //UARTx_SendByte(ch, sg_UartRx[ch].TempData);                                        //调试,将收到的数据发送出去
}

//串口中断处理
__inline static void UARTx_IRQHandler(UART_CH_Type ch)
{
        UART_TypeDef *UARTx;
       
        UARTx = (UART_TypeDef *) scg_UARTx_Base[ch];                                //获取设备基址
       
        if(UARTx->SR & BIT5)                                                                                //FIFO收到指定数据的数据了
        {
                UARTx_ReadRxData(ch, UARTx);
        }

        UARTx->CR1 |= BIT17|BIT16|BIT19|BIT20;                                                //清除中断
}

#endif //UART_DMA_EN


/*************************************************************************************************************
* 文件名:                        uart.h
* 功能:                        HC32F46X UART通讯支持
* 作者:                        cp1300@139.com
* 创建时间:                2021-06-13
* 最后修改时间:        2021-06-13
* 详细:                        2021-12-23:增加 void UARTx_EnableRx(UART_CH_Type ch, bool Enable) 串口接收使能
*************************************************************************************************************/
#ifndef _UART_H_  
#define _UART_H_
#include "hc32f46x_system.h"

/***********************配置相关************************/
#define UART_TX_TO_FIFI        1                                                        //1:数据发送到发送FIFO则认为发送完成; 0:数据从移位寄存器发送完成则认为发送完成
#define UART_ChMax                4                                                        //串口通道数量
#define UART_DMA_EN                1                                                        //是否使能DMA
#define UART_INT_PRIO        SYS_INT_UART_RX_PRO                        //中断优先级
#define UART_DMA_TX_MIN        63                                                        //使用DMA发送最少的数据,少于这个值不调用DMA,数据太少,使用DMA效率不高,但是至少3个字节以上
/*********************************************************/



//串口选择,串口0开始,到串口10
typedef enum
{
        UART_CH1        =                0,        //UART1
        UART_CH2        =                1,        //UART2
        UART_CH3        =                2,        //UART3
        UART_CH4        =                3,        //UART4
}UART_CH_Type;


//UART配置相关结构定义
typedef struct
{
        u8 OddEvenVerify;        //奇偶校验,奇,偶,无
        u8 StopBitWidth;        //停止位位宽1,2
        u8 DataBitWidth;        //数据位宽度8,9
} 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个停止位
//数据位数
#define UART_DATA_8BIT                0        //8位数据长度
#define UART_DATA_9BIT                1        //8位数据长度

//相关API
bool UARTx_Init(UART_CH_Type ch,u32 Speed, bool isEnableRx);                //串口初始化
void UARTx_EnableRx(UART_CH_Type ch, bool Enable);                                        //串口接收使能
void UARTx_SendByte(UART_CH_Type ch,u8 data);                                                //UART单字节发送
void UARTx_SendData(UART_CH_Type ch,u8 *pTxBuff,u16 DataLen);                //UART数据发送函数
void UARTx_WaitSendComplete(UART_CH_Type ch, u32 TimeOutMs);                //等待数据发送完成-从串口发送完成
void UARTx_SendString(UART_CH_Type ch,char *pString);                                //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);                                                                //清除串口接收数据计数器

#endif //_UART_H_



串口的寄存器定义,底层时钟IO等初始化就自己去根据实现。

使用代码如下:

首先初始化串口,设置接收缓冲区,不停的轮训查询接收的数据长度,两次查询的长度相同,且大

于1,则认为数据接收完成了。

void MainUartCommHandle(void)
{
        u32 cnt1,cnt2;


       
        UARTx_SetRxBuff(UART_PRINTF_CH, g_SysGlobal.MainUartBuff,  MAIN_UART_COMM_BUFF_SIZE-1);                                        //设置串口接收缓冲区
       
        while(1)
        {
                OSTimeDlyHMSM(0,0,0,100);
                cnt1 = UARTx_GetRxCnt(UART_PRINTF_CH);
                OSTimeDlyHMSM(0,0,0,50);
                cnt2 = UARTx_GetRxCnt(UART_PRINTF_CH);
                if((cnt1 == cnt2) && (cnt1 > 0))                                                                                                                                        //收到命令,这里做数据接收处理
               
                        UARTx_ClearRxCnt(UART_PRINTF_CH);                                                                                                                                //清除接收的数据
                }
               
               
        }       
}

————————————————
版权声明:本文为CSDN博主「cp1300」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cp1300/article/details/129016850

使用特权

评论回复
沙发
chenjun89| | 2023-10-17 19:46 | 只看该作者
这个在官方的Demo里面有的

使用特权

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

本版积分规则

63

主题

3316

帖子

4

粉丝