华大单片机
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;
}
}