【华大 HC32L110】调用`printf`和串口接收中断的冲突问题解决

华大单片机 HC32L110调用printf和串口接收中断的冲突问题解决,经过查找是官方库 去使能了
串口的接收功能,记录解决问题的过程

场景描述: 使能串口接收中断后,未调用 printf 之前,可以正常进接收中断,但是调用 printf 之后,进再也无法进入接收中断了

更多华大单片机的踩坑记录,可参考:
HC32L110入门踩坑记录

1.硬件MCU资料

HC32L110
适用型号:

HC32L110C6PA

HC32L110C6UA

HC32L110C4UA

HC32L110C4PA

HC32L110B6PA

HC32L110B4PA

HC32L110B6YA

网盘下载: https://pan.baidu.com/s/1ZvWNIh5osVosIL8L9xCV3Q 提取码:XYYM

2. printf和串口接收中断的冲突解决

首先,我代码中用的是串口0,IDE 使用的是 keil,调用 printf 函数使用的是微库,在工程属性的 “Target “- >”Code Generation “中勾选 ”Use MicroLIB “
在这里插入图片描述

众所周知,printf 使用微库的 stdio.h 中的接口最终调用了自己写的 fputc 函数,华大官方库的 ddl.c 库中的 fputc 函数是这么写的

以下存在的问题就是:

使能串口接收中断后,未调用 printf 之前,可以正常进接收中断,但是调用 printf 之后,进再也无法进入接收中断了

void Debug_Output(uint8_t u8Data)
{
    M0P_UART0->SCON_f.REN = 0; // 华大 ddl 库中的配置
    M0P_UART0->SBUF = u8Data;

    while (TRUE != M0P_UART0->ISR_f.TI)
    {
        ;
    }
    M0P_UART0->ICR_f.TICLR = 0;
}

int fputc(int ch, FILE *f)
{
    if (((uint8_t)ch) == '\n')
    {
        Debug_Output('\r');
    }
    Debug_Output(ch);

    return ch;
}

3.重新封装 fputc 函数

华大官方还有一个函数接口,若重新封装 fputc 函数,则可以正常 printf 之后还能进入接收中断

main 函数中重写函数:

extern int fputc_reverse(int ch, FILE *f);
int fputc_reverse(int ch, FILE *f)
{
	Uart_SendData(UARTCH0,ch);  // 要和你使用的初始化的串口对应起来,我这里用的事P35 P36 初始化为串口0
    return ch;
}

ddl.c中调用

int fputc(int ch, FILE *f)
{
    fputc_reverse(ch,f);
    return ch;
}

4.查找问题,发现是官方库配置有误

对比 Uart_SendData 函数接口以及原来官方库的对寄存器的配置发现,原来 fputc 函数中多了对寄存器的配置如下:

在这里插入图片描述

5. 查找寄存器手册,修改寄存器配置

官方 Debug_Output 函数中配置了 控制寄存器的 REN 比特为0,从寄存器手册看,配置为0则只使能了串口的发送功能,disable 了串口的接收功能,额,,那还谈何接收中断???有点坑人。。。。
在这里插入图片描述

6. 修改 Debug_Output 函数,问题得以解决

只需要将 ddl.c 文件中的 Debug_Output() 函数中,第一行代码改为以下:

void Debug_Output(uint8_t u8Data)
{
    //M0P_UART0->SCON_f.REN = 0; // 华大 ddl 库中的配置
    M0P_UART0->SCON_f.REN =1 ; // 修改后
    M0P_UART0->SBUF = u8Data;

    while (TRUE != M0P_UART0->ISR_f.TI)
    {
        ;
    }
    M0P_UART0->ICR_f.TICLR = 0;
}

int fputc(int ch, FILE *f)
{
    if (((uint8_t)ch) == '\n')
    {
        Debug_Output('\r');
    }
    Debug_Output(ch);

    return ch;
}


7.串口0初始化代码,模式1

static uint8_t uart0_rx_data[UART0_RX_LEN] = {0x00}, uart0_rx_flag = 0, uart0_rx_pos = 0;
void Uart0_TxRx_Init(uint32_t baud, func_ptr_t rxCallback)
{
    uint16_t timer = 0;
    uint32_t pclk;
    stc_uart_irq_cb_t stcUartIrqCb;

    stc_uart_config_t  stcConfig;
    stc_uart_multimode_t stcMulti;
    stc_uart_baud_config_t stcBaud;
    stc_bt_config_t stcBtConfig;

    DDL_ZERO_STRUCT(stcUartIrqCb);
    DDL_ZERO_STRUCT(stcMulti);
    DDL_ZERO_STRUCT(stcBaud);
    DDL_ZERO_STRUCT(stcBtConfig);


    Gpio_InitIOExt(3, 5, GpioDirOut, TRUE, FALSE, FALSE, FALSE);
    Gpio_InitIOExt(3, 6, GpioDirOut, TRUE, FALSE, FALSE, FALSE);

    //通道端口配置
    Gpio_SetFunc_UART0TX_P35();
    Gpio_SetFunc_UART0RX_P36();

    //外设时钟使能
    Clk_SetPeripheralGate(ClkPeripheralBt, TRUE); //模式0/2可以不使能
    Clk_SetPeripheralGate(ClkPeripheralUart0, TRUE);

    stcUartIrqCb.pfnRxIrqCb = rxCallback;
    stcUartIrqCb.pfnTxIrqCb = NULL;
    stcUartIrqCb.pfnRxErrIrqCb = NULL;
    stcConfig.pstcIrqCb = &stcUartIrqCb;
    stcConfig.bTouchNvic = TRUE;


    stcConfig.enRunMode = UartMode1;//测试项,更改此处来转换4种模式测试


    stcMulti.enMulti_mode = UartNormal;//测试项,更改此处来转换多主机模式,mode2/3才有多主机模式

    stcConfig.pstcMultiMode = &stcMulti;

    stcBaud.bDbaud = 0u;//双倍波特率功能
    stcBaud.u32Baud = baud;//更新波特率位置
    stcBaud.u8Mode = UartMode1; //计算波特率需要模式参数
    pclk = Clk_GetPClkFreq();
    timer = Uart_SetBaudRate(UARTCH0, pclk, &stcBaud);

    stcBtConfig.enMD = BtMode2;
    stcBtConfig.enCT = BtTimer;
    Bt_Init(TIM0, &stcBtConfig);//调用basetimer1设置函数产生波特率
    Bt_ARRSet(TIM0, timer);
    Bt_Cnt16Set(TIM0, timer);
    Bt_Run(TIM0);

    Uart_Init(UARTCH0, &stcConfig);
    Uart_EnableIrq(UARTCH0, UartRxIrq);
    Uart_ClrStatus(UARTCH0, UartRxFull);
    Uart_EnableFunc(UARTCH0, UartRx);
}


/**
    * @brief  获取串口0接收的内容
    *
    * @param  uart0_data:串口0结果指针; uart0_data_len:读取的长度
    *
    * @retval 1:有数据更新;0:无数据更新
    * @author yangFei
    * @date   20230814
    * @note   使用单字符队列+延时的方式;uart0_data_len不能超过最大长度
    */
uint8_t get_uart0_data(uint8_t *uart0_data, uint8_t uart0_data_len)
{
    if (uart0_rx_flag == 0 || uart0_data_len > 16)
    {
        return 0;
    }
    else
    {

        delay1ms(UART0_RX_LEN);//等待帧接收完成
        memcpy(uart0_data, uart0_rx_data, uart0_data_len);

        //清空队列和标志位
        memset(uart0_rx_data, 0, sizeof(uart0_rx_data));
        uart0_rx_pos = 0;
        uart0_rx_flag = 0;

        return 1;
    }
}

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-23 05:22:06       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-23 05:22:06       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-23 05:22:06       82 阅读
  4. Python语言-面向对象

    2024-03-23 05:22:06       91 阅读

热门阅读

  1. 在realsense D455相机上运行orb_slam3

    2024-03-23 05:22:06       41 阅读
  2. OpenCV自带颜色表

    2024-03-23 05:22:06       41 阅读
  3. cordova cordova-hot-code-push-plugin 插件热更新

    2024-03-23 05:22:06       36 阅读
  4. 图书管理借阅系统(SpringBoot项目)

    2024-03-23 05:22:06       33 阅读
  5. 【WPF应用7】 基本控件-Grid 布局的详解与示例

    2024-03-23 05:22:06       46 阅读