15962|1

58

帖子

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. 接下来看看效果
     
  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);
    }

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

     

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

     

最新回复

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

回复
举报

1668

帖子

0

TA的资源

五彩晶圆(初级)

沙发
 

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

 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
推荐帖子
摩托罗拉GSM基站安装调测考试试题(CERTIFICATION TEST OF MOTOROLA GSM BTS ICI

考试大纲1. 考试目的认证HORIZEN MACRO1800和900基站安装调测资格,测试评估基站工程师理论和实践水平。2. 考试分为笔试和现场考 ...

芯片封装概述

芯片封装 一、DIP双列直插式封装  DIP(DualIn-line Package)是指采用双列直插形式封装的集成电路芯片,绝大多数中小规模 ...

FPGA设计者的5项基本功

记得《佟林传》里,佟林练的基本功是“绕大树、解皮绳”,然后才练成了什么“鬼影随行、柳叶绵丝掌”。 在 ...

LPC1343学习笔记(连载中)--6月21日新增第十二篇

有幸拿到了EEWORLD论坛的LPC1343评估板,实在是一件意外而激励人心的事情。为感谢EEWORLD和NXP,特将学习过程与大家共同分享。也 ...

Wi-Fi 滤波器设计干货奉送(建议收藏)

本帖最后由 alan000345 于 2020-6-20 09:12 编辑 老式的 FM 收音机,从一个频道拨到另一个频道时会听到中间噼里啪啦的噪声。 ...

CC2640 纽扣电池供电设计技巧

CC2640 是具有宽电压输入范围(1.8-3.8V)的低功耗蓝牙芯片,通过内部 DCDC 或 LDO 降压 后再供给内部数字内核和电路。因为其宽 ...

解决高速串行连接面临的挑战

521600

系留、空中/水下无人机的电源解决方案你了解吗?Vicor工程师告诉你!

系留、空中/水下无人机 这类无人机由地面电源通过系线供电并控制。500V 到 800V 的高压系线传输支持更长、更细的线缆,有助于 ...

单车改装系列:改装按键

以前喇叭的按钮在太阳能板上,按下时不太方便,因此就想修改一下,把按键移动到龙头扶手上。考虑到龙头位置小,也不易固定,最后 ...

共读颁奖:《智能驾驶之激光雷达算法详解》

首先感谢网友参与 《智能驾驶之激光雷达算法详解》,以下是审核结果。 (1)获得积分奖励的网友,今日发送完毕,注意查收 ...

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

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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
快速回复 返回顶部 返回列表