小华HC32F448基本使用方法及例程


title: HC32F448基础功能使用
date: 2024.07.04

小华HC32F448基本使用方法及例程

话不多说,本人的模板例程在这,开源自行下载:

链接:https://pan.baidu.com/s/1JSSa2bRqiioi4Yr8vWp28g?pwd=r113
提取码:r113

小华单片机是一款国产的32位微处理器,

image-20240704161627332

​ 从他官网上看,这款单片机采用ARMCortex-M4内核,Vcc为1.8 ~ 3.6V,200MHz主频,内置256KB Flash,68KB SRAM,内部集成PLL,3个12-bit 2.5MSPS SAR ADC,212-bit DAC,6U(S)ART、3SPI、2I2C、2*CAN等外设,有QFN32/QFN48/LQFP48/LQFP64/LQFP80五种封装。

横向对比他的HC32F460可以看到:

image-20240704161823760

其他产品的优点这里不在赘述,详情参考官网小华半导体有限公司 (xhsc.com.cn)

具体新建项目工程不在这里讲,可以参考这篇文章[HC32F4A0PITB创建工程模板_hc32f4a0pitb软件创建-CSDN博客](https://blog.csdn.net/honmy18/article/details/139503472?ops_request_misc=%7B%22request%5Fid%22%3A%22172008141316800213047389%22%2C%22scm%22%3A%2220140713.130102334.pc%5Fall.%22%7D&request_id=172008141316800213047389&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-2-139503472-null-null.142v100pc_search_result_base3&utm_term=HC32F4 新建&spm=1018.2226.3001.4187)

本文章只对我自己的项目模板做讲解

1、GPIO输入输出

源文件如下:

#include "GPIO.h"

/**
 * @brief  LED Init
 * @param  None
 * @retval None
 */
void My_GPIO_Init(void)
{
    stc_gpio_init_t stcGpioInit;
//IO
    (void)GPIO_StructInit(&stcGpioInit);
    stcGpioInit.u16PinState = PIN_STAT_SET;
    stcGpioInit.u16PinDir = PIN_DIR_OUT;
    
    (void)GPIO_Init(LED_G_PORT, LED_G_PIN, &stcGpioInit);


//Key
    (void)GPIO_StructInit(&stcGpioInit);
    stcGpioInit.u16PinDir = PIN_DIR_IN;
    stcGpioInit.u16PullUp=PIN_PU_ON;
    
    (void)GPIO_Init(USER_PORT, USER_PIN, &stcGpioInit);
	
}


头文件如下:

#ifndef __GPIO_H_
#define __GPIO_H_

#include "hc32_ll.h"
#include "hc32_ll_gpio.h"

#define LED_G_PORT          (GPIO_PORT_B)
#define LED_G_PIN           (GPIO_PIN_05)

#define USER_PORT          (GPIO_PORT_D)
#define USER_PIN           (GPIO_PIN_02)

/* LED toggle definition */
#define LED_G_TOGGLE()      (GPIO_TogglePins(LED_G_PORT, LED_G_PIN))

#define DLY_MS              (20000UL)

void My_GPIO_Init(void);

#endif

以上是配置GPIO的相关操作,包括了LED的闪灯以及按键的输入。详细的代码可以参考官方给的参考代码,虽然读起来很枯燥,但是这是开发一款新板子的必要步骤,或者就等着别人做好现成的拿去学。需要注意的是,在主函数中,因为使用了LL库,需要在初始化代码加上这段把初始化代码括起来:

LL_PERIPH_WE(EXAMPLE_PERIPH_WE|LL_PERIPH_SEL);

//初始化代码

/* Peripheral registers write protected */
    LL_PERIPH_WP(EXAMPLE_PERIPH_WP|LL_PERIPH_SEL);

具体定义:

/* unlock/lock peripheral */
#define EXAMPLE_PERIPH_WE               (LL_PERIPH_GPIO | LL_PERIPH_EFM | LL_PERIPH_FCG | \
                                         LL_PERIPH_PWC_CLK_RMU | LL_PERIPH_SRAM)

#define EXAMPLE_PERIPH_WP               (LL_PERIPH_EFM | LL_PERIPH_FCG | LL_PERIPH_SRAM)

这部分的话可以去参考官方的文档,当然,如果你出现了类似于这种的报错的话:

..\source\main.c(165): error:  #20: identifier "stc_tmr0_init_t" is undefined
      stc_tmr0_init_t stcTmr0Init;
..\source\main.c(173): warning:  #223-D: function "TMR0_StructInit" declared implicitly
      (void)TMR0_StructInit(&stcTmr0Init);
..\source\main.c(174): error:  #20: identifier "TMR0_CLK_SRC_XTAL32" is undefined
      stcTmr0Init.u32ClockSrc     = TMR0_CLK_SRC_XTAL32;
..\source\main.c(175): error:  #20: identifier "TMR0_CLK_DIV2" is undefined
      stcTmr0Init.u32ClockDiv     = TMR0_CLK_DIV2;
..\source\main.c(176): error:  #20: identifier "TMR0_FUNC_CMP" is undefined
      stcTmr0Init.u32Func         = TMR0_FUNC_CMP;
..\source\main.c(178): warning:  #223-D: function "TMR0_Init" declared implicitly
      (void)TMR0_Init(TMR0_UNIT, TMR0_CH, &stcTmr0Init);
..\source\main.c(178): error:  #20: identifier "TMR0_CH_B" is undefined
      (void)TMR0_Init(TMR0_UNIT, TMR0_CH, &stcTmr0Init);
..\source\main.c(181): warning:  #223-D: function "TMR0_HWStopCondCmd" declared implicitly
      TMR0_HWStopCondCmd(TMR0_UNIT, TMR0_CH, ENABLE);
..\source\main.c(184): warning:  #223-D: function "TMR0_IntCmd" declared implicitly
      TMR0_IntCmd(TMR0_UNIT, TMR0_CH_INT, ENABLE);
..\source\main.c(184): error:  #20: identifier "TMR0_INT_CMP_B" is undefined
      TMR0_IntCmd(TMR0_UNIT, TMR0_CH_INT, ENABLE);

image-20240704163252988

大概率是在 hc32f4xx_conf.h 中没有开启所对应的功能导致的:

image-20240704163400990

把这个OFF改成ON,再结合一下官方的代码就可以正常编译运行了。

2、定时器相关

定时器作为一款单片机的心脏,发挥着很重要的作用,对于这款单片机来说,具体代码如下:

/* TMR0 unit and channel definition */
#define TMR0_UNIT                       (CM_TMR0_1)
#define TMR0_CLK                        (FCG2_PERIPH_TMR0_1)
#define TMR0_CH                         (TMR0_CH_B)
#define TMR0_TRIG_CH                    (AOS_TMR0)
#define TMR0_CH_INT                     (TMR0_INT_CMP_B)
#define TMR0_CH_FLAG                    (TMR0_FLAG_CMP_B)
#define TMR0_INT_SRC                    (INT_SRC_TMR0_1_CMP_B)
#define TMR0_IRQn                       (INT006_IRQn)
/* Period = 1 / (Clock freq / div) * (Compare value + 1) = 500ms */
#define TMR0_CMP_VALUE                  (XTAL32_VALUE / 16U / 2U - 1U)

其中,这句话:

image-20240704163800727

是使用外部晶振的时候需要,内部时钟的话直接在初始化里面分频就可以得到自己想要的时间周期。(本开发板的外部晶振时钟频率为8MHz)

具体代码如下:

/**
 * @brief  Configure TMR0.
 * @note   In asynchronous clock, If you want to write a TMR0 register, you need to wait for
 *         at least 3 asynchronous clock cycles after the last write operation!
 * @param  None
 * @retval None
 */
void TMR0_Config(void)
{
    stc_tmr0_init_t stcTmr0Init;
    stc_irq_signin_config_t stcIrqSignConfig;

    /* Enable timer0 and AOS clock */
    FCG_Fcg2PeriphClockCmd(TMR0_CLK, ENABLE);
    FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_AOS, ENABLE);

    /* TIMER0 configuration */
    (void)TMR0_StructInit(&stcTmr0Init);
    stcTmr0Init.u32ClockSrc     = TMR0_CLK_SRC_XTAL32;
    stcTmr0Init.u32ClockDiv     = TMR0_CLK_DIV2;
    stcTmr0Init.u32Func         = TMR0_FUNC_CMP;
    stcTmr0Init.u16CompareValue = (uint16_t)TMR0_CMP_VALUE;
    (void)TMR0_Init(TMR0_UNIT, TMR0_CH, &stcTmr0Init);
    /* Asynchronous clock source, writing to TMR0 register requires waiting for three asynchronous clocks. */
    DDL_DelayMS(1000U);
    TMR0_HWStopCondCmd(TMR0_UNIT, TMR0_CH, ENABLE);
    /* Asynchronous clock source, writing to TMR0 register requires waiting for three asynchronous clocks. */
    DDL_DelayMS(1000U);
    TMR0_IntCmd(TMR0_UNIT, TMR0_CH_INT, ENABLE);
    // /* Asynchronous clock source, writing to TMR0 register requires waiting for three asynchronous clocks. */
    DDL_DelayMS(1000U);
    AOS_SetTriggerEventSrc(TMR0_TRIG_CH, BSP_KEY_KEY5_EVT);

    /* Interrupt configuration */
    stcIrqSignConfig.enIntSrc    = TMR0_INT_SRC;
    stcIrqSignConfig.enIRQn      = TMR0_IRQn;
    stcIrqSignConfig.pfnCallback = &TMR0_CompareIrqCallback;
    (void)INTC_IrqSignIn(&stcIrqSignConfig);
    NVIC_ClearPendingIRQ(stcIrqSignConfig.enIRQn);
    NVIC_SetPriority(stcIrqSignConfig.enIRQn, DDL_IRQ_PRIO_DEFAULT);
    NVIC_EnableIRQ(stcIrqSignConfig.enIRQn);
}

/**
 * @brief  Configure XTAL32.
 * @param  None
 * @retval None
 */
void XTAL32_Config(void)
{
    stc_clock_xtal32_init_t stcXtal32Init;

    (void)CLK_Xtal32StructInit(&stcXtal32Init);
    /* Configure Xtal32 */
    stcXtal32Init.u8State   = CLK_XTAL32_ON;
    stcXtal32Init.u8Drv     = CLK_XTAL32_DRV_MID;
    stcXtal32Init.u8Filter  = CLK_XTAL32_FILTER_OFF;

    GPIO_AnalogCmd(BSP_XTAL32_PORT, BSP_XTAL32_IN_PIN | BSP_XTAL32_OUT_PIN, ENABLE);
    (void)CLK_Xtal32Init(&stcXtal32Init);
}

image-20240704164007840

这块代码是为了使用外部晶振写的初始化代码,使用内部晶振的话不用考虑。其中,初始化的代码当中,这部分很重要:

image-20240704164136910

第一个参数是你使用的始终源,最后一个参数是比较值,也就是算周期要用的值,上面define过。

需要特别注意的是:当你使用外部晶振时,在异步时钟下,必须保证所选的异步时钟开启;否则TMR0将不会计数,配置也不一定准确。异步时钟下,对TMR0寄存器操作需要3个异步时钟才能写入。因此,代码这部分的延时是充分考虑到这个因素:

我只能说,这种初始化延时的不确定因素真的会在很关键的时候要你的命。原因可能是内核,反正我见过好多国产的单片机都有这个通病,很离谱。

3、U(S)ART相关

相关define如下:

/* 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)

/* USART unit definition */
#define USART_UNIT                      (CM_USART2)
#define USART_FCG_ENABLE()              (FCG_Fcg3PeriphClockCmd(FCG3_PERIPH_USART2, ENABLE))

/* USART interrupt definition */
#define USART_RX_ERR_IRQn               (INT000_IRQn)
#define USART_RX_ERR_INT_SRC            (INT_SRC_USART2_EI)

主函数初始化时要加上这句:

BSP_CLK_Init();//务必保留

之后就是正常的初始化程序:

/* Configure USART RX/TX pin. */
    GPIO_SetFunc(USART_RX_PORT, USART_RX_PIN, USART_RX_GPIO_FUNC);
    GPIO_SetFunc(USART_TX_PORT, USART_TX_PIN, USART_TX_GPIO_FUNC);

    /* Enable peripheral clock */
    USART_FCG_ENABLE();

    /* 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)) {
        for (;;) {
        }
    }
    
    /* Register error IRQ handler && configure NVIC. */
    stcIrqSigninConfig.enIRQn = USART_RX_ERR_IRQn;
    stcIrqSigninConfig.enIntSrc = USART_RX_ERR_INT_SRC;
    stcIrqSigninConfig.pfnCallback = &USART_RxError_IrqCallback;
    (void)INTC_IrqSignIn(&stcIrqSigninConfig);
    NVIC_ClearPendingIRQ(stcIrqSigninConfig.enIRQn);
    NVIC_SetPriority(stcIrqSigninConfig.enIRQn, DDL_IRQ_PRIO_DEFAULT);
    NVIC_EnableIRQ(stcIrqSigninConfig.enIRQn);

    /* Peripheral registers write protected */
    LL_PERIPH_WP(EXAMPLE_PERIPH_WP|LL_PERIPH_SEL);

    /* Enable RX/TX function */
    USART_FuncCmd(USART_UNIT, (USART_RX | USART_INT_RX | USART_TX), ENABLE);

我在定时器里面做了收发的相关操作:

#if (MASTER_DEVICE_ENABLE == DDL_ON)
    USART_WriteData(USART_UNIT, 0x0A);
#else
    if (SET == USART_GetStatus(USART_UNIT, USART_FLAG_RX_FULL)) {
            flag = USART_ReadData(USART_UNIT);
            while (RESET == USART_GetStatus(USART_UNIT, USART_FLAG_TX_EMPTY));
            if(flag==0x0A)LED_G_TOGGLE();
            else GPIO_ResetPins(LED_G_PORT, LED_G_PIN);
            USART_WriteData(USART_UNIT, flag);
    }
#endif

主要设置了主从机的相关代码,只需要改预编译的define就行。内容就是一个主机发特定字符,另一个从机接受后判断是否为这个字符后进行GPIO反转电平的操作实现LED闪灯。

image-20240704171302356

这部分代码是设置波特率等相关寄存器的操作。

总的来说,如果你有单片机开发的基础的话,不用担心会有多难,只不过是时间问题。其他功能的话还有待开发,等用到的时候再写吧。

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好,以下是使用STM32单片机控制HC-SR04超声波避障的例程: #include "stm32f10x.h" #include "delay.h" #include "usart.h" #include "stdio.h" #define TRIG_PIN GPIO_Pin_0 #define ECHO_PIN GPIO_Pin_1 #define TRIG_PORT GPIOA #define ECHO_PORT GPIOA void HC_SR04_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = TRIG_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(TRIG_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = ECHO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(ECHO_PORT, &GPIO_InitStructure); } float HC_SR04_GetDistance(void) { uint32_t time; float distance; GPIO_ResetBits(TRIG_PORT, TRIG_PIN); delay_us(2); GPIO_SetBits(TRIG_PORT, TRIG_PIN); delay_us(10); GPIO_ResetBits(TRIG_PORT, TRIG_PIN); while(GPIO_ReadInputDataBit(ECHO_PORT, ECHO_PIN) == RESET); time = 0; while(GPIO_ReadInputDataBit(ECHO_PORT, ECHO_PIN) == SET) { time++; delay_us(1); } distance = (float)time * 0.017; return distance; } int main(void) { float distance; HC_SR04_Init(); USART1_Init(); while(1) { distance = HC_SR04_GetDistance(); printf("Distance: %.2f cm\r\n", distance); delay_ms(500); } } 这个例程中,我们使用了STM32的GPIO口来控制HC-SR04超声波模块的触发和接收引脚,并通过计算超声波的回波时间来计算距离。在主函数中,我们不断地获取距离并通过串口输出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值