3952|1

51

帖子

0

TA的资源

一粒金砂(中级)

【小华HC32F448测评】关于小华半导体的UART中断发送和PRINTF构造和重定向 [复制链接]

本帖最后由 学学学学学学学 于 2023-8-11 16:25 编辑
  1. 先打开例程文件夹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)
    

     

  2. 解释一下main.c里其他宏定义

    1. 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)
      
    2. 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   */
  3. 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)
    
  4. 主函数里的代码:
    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);
            }
        }
    }
  5. 接下来看看效果
    image.png  
  6. 接下来我们可以简单的构造一个自己写的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);
    }

    image.png

  7. 然后也可以使用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这个函数里面已经自动默认了,所以我们需要把头文件里的定义改变。
    image.png  

    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);
        }
    }

    image.png  

最新回复

BSP_PRINTF_Preinit这个函数里面自动默认东西还不少   详情 回复 发表于 2023-8-13 21:54

回复
举报

1580

帖子

0

TA的资源

五彩晶圆(初级)

BSP_PRINTF_Preinit这个函数里面自动默认东西还不少


回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
推荐帖子
世界各国电视制式

PDF格式的哦

北航单片机视频教程   哈哈!!

北航单片机视频教程多媒体教程 下面是用户共享的文件列表,安装eMule后,您可以点击这些文件名进行下载 .1K2K.rm 161.9MB . ...

【TI原创】LM3S以太网远程升级带加密解决方案

第一步:程序发布 72436 第二步:下载升级 72437 第三步,程序内部的加密算法,我是将设备的MAC地址加一个随机数通过MD5算法 ...

低功耗MCU哪家强?不找Atmel你找谁?正宗低功耗血统

低功耗MCU哪家强?不找Atmel你找谁?正宗低功耗血统!杠杠滴:handshake 在过去的十多年里,Atmel一直专注于低功耗技术的研 ...

炼狱传奇-音乐播放器之战

炼狱传奇-音乐播放器之战

【GD32E503评测】+ littlegl移植到GD32E503V-EVAL

LittlevGL移植到GD32E503V 前面已经成功的移植了tos到GD32E503V处理器上,基于tos,将LittlevGL移植到GD32E503V-eval开发板。 ...

请问一下液晶屏显示模式中的2段码、3段码 、4段码与数码管的8段码有什么区别

本帖最后由 深圳小花 于 2021-10-12 15:21 编辑 看到一液晶屏资料中提示 566443 请问一下液晶屏显示模式中的2 ...

STM8中文参考手册

STM8中文参考手册

单片机原理及应用详解

单片机在我们的生活中随处可见,那么单片机原理又是什么?它是如何工作的?单片机有哪些应用呢?今天就和大家分享一下单片机原理及 ...

关闭
站长推荐上一条 1/10 下一条

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表