|
【小华HC32F448测评】关于小华半导体的UART中断发送和PRINTF构造和重定向
[复制链接]
本帖最后由 学学学学学学学 于 2023-8-11 16:25 编辑
- 先打开例程文件夹examples中的usart文件夹,然后打开usart_uart_int文件夹,打开MDK例程工程。这个例程的做用是:在电脑上用串口助手通过RX发送数据给HC32F448,然后开发板会通过TX发送给串口助手一模一样的数据并显示。
首先定义开发板上和串口助手连接的串口为USART2
/* USART RX/TX pin definition */
#define USART_RX_PORT (GPIO_PORT_C) /* PC11: USART2_RX */
#define USART_RX_PIN (GPIO_PIN_11)
#define USART_RX_GPIO_FUNC (GPIO_FUNC_37)
#define USART_TX_PORT (GPIO_PORT_C) /* PC10: USART2_TX */
#define USART_TX_PIN (GPIO_PIN_10)
#define USART_TX_GPIO_FUNC (GPIO_FUNC_36)
-
解释一下main.c里其他宏定义
-
LL_PERIPH
LL_PERIPH的源文件和头文件追寻在hc32_ll.c和hc32_ll.h里,ll指的不是IIC,是low_level,LL库。Peripheral/PERIPH在单片机中的意思指的是外设,在这里显然是用来控制外设。
#define LL_PERIPH_SEL (LL_PERIPH_GPIO | LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU | \
LL_PERIPH_EFM | LL_PERIPH_SRAM)
在hc32_ll.h头文件中可以发现SEL这个宏定义的参考来源是这个ALL,显然这个指令是用来形成一个8位二进制值,用以开启或者关闭LL_PERIPH,诸如EFM,FCG,和GPIO等定义是依次令八位二进制0000 0001,依次左移。如果这些值全部进行或运算,会形成1111 1111,即0xFF,如果是如SEL这样进行或运算,就会形成1100 0111。
#define LL_PERIPH_EFM (1UL << 0U)
#define LL_PERIPH_FCG (1UL << 1U)
#define LL_PERIPH_GPIO (1UL << 2U)
#define LL_PERIPH_INTC (1UL << 3U)
#define LL_PERIPH_LVD (1UL << 4U)
#define LL_PERIPH_MPU (1UL << 5U)
#define LL_PERIPH_PWC_CLK_RMU (1UL << 6U)
#define LL_PERIPH_SRAM (1UL << 7U)
#define LL_PERIPH_ALL (LL_PERIPH_EFM | LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC | \
LL_PERIPH_LVD | LL_PERIPH_MPU | LL_PERIPH_SRAM | LL_PERIPH_PWC_CLK_RMU)
- USART
显然这里的宏定义是用来初始化USART,我们打开BSP和usart的头文件和源文件就会有类似定义。FCG_Fcg3PeriphClockCmd(FCG3_PERIPH_USART2, ENABLE)是用来打开FCG3中USART2的时钟。hc32f448.h头文件中USART2的中断源编号,这些中断源包括发送完成中断(USART_INT_TX_CPLT)、发送结束中断(USART_INT_TENDI)、发送错误中断(USART_INT_TCI)、发送中断(USART_INT_TI)、接收错误中断(INT_SRC_USART2_EI)接收超时中断(USART_INT_RTO)、接收中断(USART_INT_RI)。INT000等是中断事件源编号。
/* USART unit definition */
#define USART_UNIT (CM_USART2)
#define USART_FCG_ENABLE() (FCG_Fcg3PeriphClockCmd(FCG3_PERIPH_USART2, ENABLE))
/* USART Tx complete flag select: USART_INT_TX_CPLT or USART_INT_TX_END */
#define USART_TX_CPLT_FLAG (USART_INT_TX_CPLT)
/* USART interrupt definition */
#define USART_RX_ERR_IRQn (INT000_IRQn)
#define USART_RX_ERR_INT_SRC (INT_SRC_USART2_EI)
#define USART_RX_FULL_IRQn (INT001_IRQn)
#define USART_RX_FULL_INT_SRC (INT_SRC_USART2_RI)
#define USART_TX_EMPTY_IRQn (INT002_IRQn)
#define USART_TX_EMPTY_INT_SRC (INT_SRC_USART2_TI)
#define USART_TX_CPLT_IRQn (INT003_IRQn)
#if (USART_INT_TX_CPLT == USART_TX_CPLT_FLAG)
#define USART_TX_CPLT_INT_SRC (INT_SRC_USART2_TCI)
#elif (USART_INT_TX_END == USART_TX_CPLT_FLAG)
#define USART_TX_CPLT_INT_SRC (INT_SRC_USART2_TENDI)
#else
#error "USART_TX_CPLT_FLAG defined error"
#endif
//hc32f488.h头文件中关于USART2的定义。
INT_SRC_USART2_EI = 328U, /* USART_2_EI */
INT_SRC_USART2_RI = 329U, /* USART_2_RI */
INT_SRC_USART2_TI = 330U, /* USART_2_TI */
INT_SRC_USART2_RTO = 331U, /* USART_2_RTO */
INT_SRC_USART2_TENDI = 332U, /* USART_2_TENDI */
INT_SRC_USART2_TCI = 333U, /* USART_2_TCI */
- main.c中其他自定义函数
static void USART_TxEmpty_IrqCallback(void)//用来发送寄存器数据的中断函数,在数组BUF没有清空前,持续发送一字节数据
static void USART_TxComplete_IrqCallback(void)//中断发送数据完成后的回调函数
static void USART_RxFull_IrqCallback(void)//接收数据的回调中断函数
static void USART_RxError_IrqCallback(void)//接收数据发生错误,对中断状态进行重置
static void INTC_IrqInstalHandler(const stc_irq_signin_config_t *pstcConfig, uint32_t u32Priority)//对所需中断函数进行配置和设置优先级。
其中INTC_IrqInstalHandler(const stc_irq_signin_config_t *pstcConfig, uint32_t u32Priority)这个函数就是用来配置中断和对应中断回调函数以及其优先级的。
这个函数里面设置优先级的指令为NVIC_SetPriority(pstcConfig->enIRQn, u32Priority);
这个函数的形参输入格式为:INTC_IrqInstalHandler(&stcIrqSigninConfig, DDL_IRQ_PRIO_XX); 其中指针定义为:stc_irq_signin_config_t stcIrqSigninConfig;
Priority的参数输入为DDL_IRQ_PRIO_00至DDL_IRQ_PRIO_15.
*/
/**
* @defgroup INTC_Priority_Sel Interrupt Priority Level 00 ~ 15
* @{
*/
#define DDL_IRQ_PRIO_00 (0U)
#define DDL_IRQ_PRIO_01 (1U)
#define DDL_IRQ_PRIO_02 (2U)
#define DDL_IRQ_PRIO_03 (3U)
#define DDL_IRQ_PRIO_04 (4U)
#define DDL_IRQ_PRIO_05 (5U)
#define DDL_IRQ_PRIO_06 (6U)
#define DDL_IRQ_PRIO_07 (7U)
#define DDL_IRQ_PRIO_08 (8U)
#define DDL_IRQ_PRIO_09 (9U)
#define DDL_IRQ_PRIO_10 (10U)
#define DDL_IRQ_PRIO_11 (11U)
#define DDL_IRQ_PRIO_12 (12U)
#define DDL_IRQ_PRIO_13 (13U)
#define DDL_IRQ_PRIO_14 (14U)
#define DDL_IRQ_PRIO_15 (15U)
- 主函数里的代码:
static uint8_t m_au8DataBuf[RING_BUF_SIZE];
static stc_ring_buf_t m_stcRingBuf;
static __IO en_flag_status_t m_enTxCompleteFlag = SET;
int32_t main(void)
{
// stc_usart_uart_init_t stcUartInit;
stc_irq_signin_config_t stcIrqSigninConfig;
/* MCU Peripheral registers write unprotected */
LL_PERIPH_WE(LL_PERIPH_SEL);//解除外设寄存器,允许 写 入外设寄存器
/* Initialize BSP system clock. */
BSP_CLK_Init();
/* Initialize BSP expand IO. */
BSP_IO_Init();
/* Initialize BSP LED. */
BSP_LED_Init();
/* Configure USART RX/TX pin. */
GPIO_SetFunc(USART_RX_PORT, USART_RX_PIN, USART_RX_GPIO_FUNC);//配置USART2两个引脚的功能
GPIO_SetFunc(USART_TX_PORT, USART_TX_PIN, USART_TX_GPIO_FUNC);
/* Enable peripheral clock */
USART_FCG_ENABLE();
/* Initialize ring buffer function. */
(void)BUF_Init(&m_stcRingBuf, m_au8DataBuf, sizeof(m_au8DataBuf));//初始化一个数组,并让它stcRingBus这个结构体绑定
/* Initialize UART. */
(void)USART_UART_StructInit(&stcUartInit);
stcUartInit.u32ClockDiv = USART_CLK_DIV64;//设置分配系数
stcUartInit.u32Baudrate = 115200UL;//设置波特率
stcUartInit.u32OverSampleBit = USART_OVER_SAMPLE_8BIT;
if (LL_OK != USART_UART_Init(USART_UNIT, &stcUartInit, NULL)) {//初始化uart2
BSP_LED_On(LED_RED);//如果初始化发送错误点亮板上红灯
for (;;) {
}
}
/* Register RX error IRQ handler && configure NVIC. */
stcIrqSigninConfig.enIRQn = USART_RX_ERR_IRQn;
stcIrqSigninConfig.enIntSrc = USART_RX_ERR_INT_SRC;
stcIrqSigninConfig.pfnCallback = &USART_RxError_IrqCallback;
INTC_IrqInstalHandler(&stcIrqSigninConfig, DDL_IRQ_PRIO_DEFAULT);//设置接受错误中断函数
/* Register RX full IRQ handler && configure NVIC. */
stcIrqSigninConfig.enIRQn = USART_RX_FULL_IRQn;
stcIrqSigninConfig.enIntSrc = USART_RX_FULL_INT_SRC;
stcIrqSigninConfig.pfnCallback = &USART_RxFull_IrqCallback;
INTC_IrqInstalHandler(&stcIrqSigninConfig, DDL_IRQ_PRIO_DEFAULT);//设置接收中断函数
/* Register TX empty IRQ handler && configure NVIC. */
stcIrqSigninConfig.enIRQn = USART_TX_EMPTY_IRQn;
stcIrqSigninConfig.enIntSrc = USART_TX_EMPTY_INT_SRC;
stcIrqSigninConfig.pfnCallback = &USART_TxEmpty_IrqCallback;
INTC_IrqInstalHandler(&stcIrqSigninConfig, DDL_IRQ_PRIO_DEFAULT);//设置发送中断函数
/* Register TX complete IRQ handler && configure NVIC. */
stcIrqSigninConfig.enIRQn = USART_TX_CPLT_IRQn;
stcIrqSigninConfig.enIntSrc = USART_TX_CPLT_INT_SRC;
stcIrqSigninConfig.pfnCallback = &USART_TxComplete_IrqCallback;
INTC_IrqInstalHandler(&stcIrqSigninConfig, DDL_IRQ_PRIO_DEFAULT);//设置发送完成中断函数
/* MCU Peripheral registers write protected */
LL_PERIPH_WP(LL_PERIPH_SEL);//重新禁止对外设寄存器进行读写操作
/* Enable RX function */
USART_FuncCmd(USART_UNIT, (USART_RX | USART_INT_RX), ENABLE);//允许USART2接收
for (;;) {
DDL_DelayMS(200);
if ((SET == m_enTxCompleteFlag) && !BUF_Empty(&m_stcRingBuf)) {
m_enTxCompleteFlag = RESET;
#if (USART_INT_TX_END == USART_TX_CPLT_FLAG)
USART_FuncCmd(USART_UNIT, USART_INT_TX_END, DISABLE);
#endif
USART_FuncCmd(USART_UNIT, (USART_TX | USART_INT_TX_EMPTY), ENABLE);
}
}
}
- 接下来看看效果
- 接下来我们可以简单的构造一个自己写的printf函数,在主循环里放上my_printf(“Hello,World");
/**
* [url=home.php?mod=space&uid=159083]@brief[/url] printf a word
* @param None
* @retval None
*/
static void my_printf(char ch[])
{
int8_t i=0;
uint8_t u8Data1=ch[0];
(void)BUF_Write(&m_stcRingBuf, &u8Data1, 1UL);
i++;
while(ch[i]!='\0')
{
u8Data1=ch[i];
(void)BUF_Write(&m_stcRingBuf, &u8Data1, 1UL);
i++;
}
u8Data1=0x0A;
(void)BUF_Write(&m_stcRingBuf, &u8Data1, 1UL);
}
-
然后也可以使用LL库和BSP里面重定向的函数
首先我们要先打开hc32f4xx_conf.h,令#define LL_PRINT_ENABLE (DDL_ON)
然后我们打开ev_hc32f448_lqfp80.c,然后找到PRINTF相关的内容,可以看到一个BSP_PRINTF_Preinit 函数,输入的两个形参,一个是设备指针,一个是设备参数,void *vpDevice,这个形参的定义是void型,意思是我们可以输入任意类型指针,通常我们在这里可以直接把 USART、SPI 、I2C的指针参数填进去,例如:令vpDevice = CM_USART2 ,CM_USART2 = ((CM_USART_TypeDef *)CM_USART2_BASE)。设备参数指的是波特率,通常为115200.
但是我们要注意,实际上这个函数已经在ev_hc32f448_lqfp80.h头文件在149行定义了用哪个USART和波特率,
/**
* @defgroup BSP_PRINT_CONFIG BSP PRINT Configure definition
* @{
*/
#define BSP_PRINTF_DEVICE (CM_USART2)
#define BSP_PRINTF_DEVICE_FCG (FCG3_PERIPH_USART2)
#define BSP_PRINTF_BAUDRATE (115200UL)
#define BSP_PRINTF_BAUDRATE_ERR_MAX (0.025F)
#define BSP_PRINTF_PORT (GPIO_PORT_C)
#define BSP_PRINTF_PIN (GPIO_PIN_10)
#define BSP_PRINTF_PORT_FUNC (GPIO_FUNC_36)
/**
* @brief BSP printf device, clock and port pre-initialize.
* @param [in] vpDevice Pointer to print device
//例如:令vpDevice = CM_USART2 ((CM_USART_TypeDef *)CM_USART2_BASE)
* @param [in] u32Baudrate Print device communication baudrate
* @retval int32_t:
* - LL_OK: Initialize successfully.
* - LL_ERR: Initialize unsuccessfully.
* - LL_ERR_INVD_PARAM: The u32Baudrate value is 0.
*/
int32_t BSP_PRINTF_Preinit(void *vpDevice, uint32_t u32Baudrate)
{
uint32_t i;
float32_t f32Error;
const uint32_t au32Div[] = {USART_CLK_DIV1, USART_CLK_DIV4, USART_CLK_DIV16, USART_CLK_DIV64,
USART_CLK_DIV128, USART_CLK_DIV256, USART_CLK_DIV512, USART_CLK_DIV1024
};
stc_usart_uart_init_t stcUartInit;
int32_t i32Ret = LL_ERR_INVD_PARAM;
(void)vpDevice;
if (0UL != u32Baudrate) {
/* Set TX port function */
GPIO_SetFunc(BSP_PRINTF_PORT, BSP_PRINTF_PIN, BSP_PRINTF_PORT_FUNC);
/* Enable clock */
FCG_Fcg3PeriphClockCmd(BSP_PRINTF_DEVICE_FCG, ENABLE);
/* Configure UART */
(void)USART_UART_StructInit(&stcUartInit);
stcUartInit.u32OverSampleBit = USART_OVER_SAMPLE_8BIT;
(void)USART_UART_Init(BSP_PRINTF_DEVICE, &stcUartInit, NULL);
for (i = 0UL; i < ARRAY_SZ(au32Div); i++) {
USART_SetClockDiv(BSP_PRINTF_DEVICE, au32Div[i]);
i32Ret = USART_SetBaudrate(BSP_PRINTF_DEVICE, u32Baudrate, &f32Error);
if ((LL_OK == i32Ret) && \
((-BSP_PRINTF_BAUDRATE_ERR_MAX <= f32Error) && (f32Error <= BSP_PRINTF_BAUDRATE_ERR_MAX))) {
USART_FuncCmd(BSP_PRINTF_DEVICE, USART_TX, ENABLE);
break;
} else {
i32Ret = LL_ERR;
}
}
}
return i32Ret;
}
然后我们再打开hc32_ii_utility.h和.c文件,这个函数是LL库对printf()函数的初始化,我们只需要对这个函数进行初始化,就可以直接使用printf()或者DDL_Printf(),
int32_t LL_PrintfInit(void *vpDevice, uint32_t u32Param, int32_t (*pfnPreinit)(void *vpDevice, uint32_t u32Param));
现在我们把main主函数改为如下即可,效果就像下面。我们只需要一条指令即可:DDL_PrintfInit(CM_USART2,115200,BSP_PRINTF_Preinit);
但是注意这里只能用USART2,和波特率只能是115200,原因在于BSP_PRINTF_Preinit这个函数里面已经自动默认了,所以我们需要把头文件里的定义改变。
int32_t main(void)
{
/* MCU Peripheral registers write unprotected */
LL_PERIPH_WE(LL_PERIPH_SEL);
/* Initialize BSP system clock. */
BSP_CLK_Init();
/* Initialize BSP expand IO. */
BSP_IO_Init();
/* Initialize BSP LED. */
BSP_LED_Init();
DDL_PrintfInit(CM_USART2,115200,BSP_PRINTF_Preinit);//初始化uart2
// DDL_PrintfInit(BSP_PRINTF_DEVICE, BSP_PRINTF_BAUDRATE, BSP_PRINTF_Preinit);
for (;;) {
DDL_Printf("Hello,World_n\n");
printf("Hello,World_p");
DDL_DelayMS(100);
}
}
|
|