华大单片机
HC32L110
调用printf
和串口接收中断的冲突问题解决,经过查找是官方库 去使能了
串口的接收功能,记录解决问题的过程
目录
- 1.硬件MCU资料
- 2. printf和串口接收中断的冲突解决
- 3.重新封装 fputc 函数
- 4.查找问题,发现是官方库配置有误
- 5. 查找寄存器手册,修改寄存器配置
- 6. 修改 Debug_Output 函数,问题得以解决
- 7.串口0初始化代码,模式1
场景描述: 使能串口接收中断后,未调用 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 初始化为串口0return 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;}
}