• 潘多拉 IOT 开发板学习(HAL 库)—— 实验2 蜂鸣器实验(学习笔记)


    本文代码参考正点原子例程

    实验功能

    例程源码:(main.c)

    该实验实现了蜂鸣器的不停鸣叫,和 LED 实验一样,主要涉及的知识点是操作 GPIO。

    #include "sys.h"
    #include "delay.h"
    #include "led.h"
    #include "beep.h"
    
    /*********************************************************************************
    			  ___   _     _____  _____  _   _  _____  _____  _   __
    			 / _ \ | |   |_   _||  ___|| \ | ||_   _||  ___|| | / /
    			/ /_\ \| |     | |  | |__  |  \| |  | |  | |__  | |/ /
    			|  _  || |     | |  |  __| | . ` |  | |  |  __| |    \
    			| | | || |_____| |_ | |___ | |\  |  | |  | |___ | |\  \
    			\_| |_/\_____/\___/ \____/ \_| \_/  \_/  \____/ \_| \_/
    
     *	******************************************************************************
     *	正点原子 Pandora STM32L475 IoT开发板	实验2
     *	蜂鸣器实验	HAL库版本
     *	技术支持:www.openedv.com
     *	淘宝店铺:http://openedv.taobao.com
     *	关注微信公众平台微信号:"正点原子",免费获取STM32资料。
     *	广州市星翼电子科技有限公司
     *	作者:正点原子 @ALIENTEK
     *	******************************************************************************/
    
    int main(void)
    {
        HAL_Init();
        SystemClock_Config();	//初始化系统时钟为80M
        delay_init(80); 		//初始化延时函数    80M系统时钟
    
        LED_Init();				//初始化LED
        BEEP_Init();			//初始化蜂鸣器
    
        while(1)
        {
            BEEP(1);
            delay_ms(500);
            BEEP(0);
            delay_ms(1000);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    代码剖析

    HAL_Init()

    HAL_Init() 定义如下:(具体实现的功能见注释)

    HAL_StatusTypeDef HAL_Init(void)
    {
      HAL_StatusTypeDef  status = HAL_OK;
    
      /* 配置 Flash 预取,指令缓存,数据缓存        */
      /* 默认配置为:预存取关闭 指令缓存和数据缓存开启 */     
    #if (INSTRUCTION_CACHE_ENABLE == 0)  // Flash开启预存取配置,能加速CPU代码的执行
       __HAL_FLASH_INSTRUCTION_CACHE_DISABLE();
    #endif /* INSTRUCTION_CACHE_ENABLE */
    
    #if (DATA_CACHE_ENABLE == 0)
       __HAL_FLASH_DATA_CACHE_DISABLE();
    #endif /* DATA_CACHE_ENABLE */ 
    
    #if (PREFETCH_ENABLE != 0)
      __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
    #endif /* PREFETCH_ENABLE */
    
      /* Set Interrupt Group Priority */
      HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2); // 配置 NVIC 优先级分组
    
      /* Use SysTick as time base source and configure 1ms tick (default clock after Reset is MSI) */
      if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK) //初始化滴答定时器,时钟节拍设置为 1ms
      {
        status = HAL_ERROR;
      }
      else
      {
        /* Init the low level hardware */
        HAL_MspInit(); // 低速的外设初始化,比如 GPIO、中断等的设置(使用 STM32CubeMx 生成代码时会将低速外设初始
                       // 代码当这类函数里,其他情况下可以忽略这个函数
      }
    
      /* Return function status */
      return status;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    HAL_InitTick()
    滴答定时器时钟节拍初始化函数

    __weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    {
      HAL_StatusTypeDef  status = HAL_OK;
    
      /*Configure the SysTick to have interrupt in 1ms time basis*/
      if (HAL_SYSTICK_Config(SystemCoreClock/1000UL) != 0U) // 系统时钟/1000,中断周期为 1ms
      {
        status = HAL_ERROR;
      }
      else
      {
        /*Configure the SysTick IRQ priority */
        HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0); // 将滴答定时器的中断优先级设置为最高
      }
    
      /* Return function status */
      return status;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    SystemClock_Config()

    SystemClock_Config()函数定义如下:(具体实现的功能见注释,仅供参考)

    void SystemClock_Config(void)
    {
        HAL_StatusTypeDef	ret = HAL_OK;
    
        RCC_OscInitTypeDef RCC_OscInitStruct; // 定义振荡器初始化结构体变量
        RCC_ClkInitTypeDef RCC_ClkInitStruct; // 定义时钟初始化结构体变量
    
        __HAL_RCC_PWR_CLK_ENABLE(); // 使能电源控制时钟
    
        /*Initializes the CPU, AHB and APB busses clocks*/
        RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 将 HSE(外部高速时钟)作为时钟源
        RCC_OscInitStruct.HSEState = RCC_HSE_ON;  // 开启 HSE
        RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 开启 PLL(锁相环)
        RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // 将 HSE 作为 PLL 的时钟源
        RCC_OscInitStruct.PLL.PLLM = 1; // PLL-VCO 输入时钟分频系数,1 表示 2 分频(8 / 2 = 4M,本开发板外部晶振频率为 8MHz)
        RCC_OscInitStruct.PLL.PLLN = 20; // PLL-VCO 输出时钟倍频系数,4 * 20 = 80M,即输出时钟频率为 80MHz
        RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // SAI 时钟的分频系数
        RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // SDMMC1, RNG 和 USB 的时钟分频系数
        RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // 主系统时钟的分频系数
    
        ret = HAL_RCC_OscConfig(&RCC_OscInitStruct); //初始化时钟配置
    
        if(ret != HAL_OK)	while(1);
    
        /*Initializes the CPU, AHB and APB busses clocks*/
        RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                      | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; // 将所有时钟同时进行配置
        RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 将 PLL 作为系统时钟源
        RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB 不分频
        RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // APB1 不分频
        RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2 不分频
    
    
        ret	= HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4); // 配置时钟初始结构体变量,
        //使用 Flash 延迟4,等待状态(延迟)的数量需要根据CPU时钟(HCLK)的频率和内部电压范围来选择,具体怎么
        //选需要参考芯片手册
    
        if(ret != HAL_OK)	while(1);
    
        /*Configure the main internal regulator output voltage*/
        ret = HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); //内部寄存器输出电压配置
        // 下面是 HAL_PWREx_ControlVoltageScaling() 函数说明的部分内容:
        //PWR_REGULATOR_VOLTAGE_SCALE1 Regulator voltage output range 1 mode, typical output voltage
        // at 1.2 V, system frequency up to 80 MHz.
    
        if(ret != HAL_OK)	while(1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    delay_init()

    滴答定时器已经在 HAL_Init() 中进行了初始化,下面这个函数实际上就是给 fac_us 赋了一个值(目前暂不涉及操作系统,其他代码暂时不去研究)。

    static u32 fac_us = 0;							//us延时倍乘数
    /**
     * @brief	初始化延迟函数,SYSTICK的时钟固定为AHB时钟
     *
     * @param   SYSCLK	系统时钟频率
     *
     * @return  void
     */
    void delay_init(u8 SYSCLK)
    {
    #if SYSTEM_SUPPORT_OS 						//如果需要支持OS.
        u32 reload;
    #endif
        HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
        fac_us = SYSCLK;						//不论是否使用OS,fac_us都需要使用
    
    #if SYSTEM_SUPPORT_OS 						//如果需要支持OS.
        reload = SYSCLK;					  //每秒钟的计数次数 单位为K
        reload *= 1000000 / delay_ostickspersec;	//根据delay_ostickspersec设定溢出时间
        //reload为24位寄存器,最大值:16777216,在80M下,约209.7ms左右
        fac_ms = 1000 / delay_ostickspersec;		//代表OS可以延时的最少单位
        SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
        SysTick->LOAD = reload; 					//每1/OS_TICKS_PER_SEC秒中断一次
        SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
    #else
    #endif
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    BEEP_Init()

    /**
     * @brief	蜂鸣器 IO初始化函数
     *
     * @param   void
     *
     * @return  void
     */
    void BEEP_Init(void)
    {
        GPIO_InitTypeDef GPIO_InitStruct;  // 定义一个GPIO初始化结构体变量
    
        __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOE的时钟
    
        //PB2
        GPIO_InitStruct.Pin = GPIO_PIN_2; // 设置对应的引脚
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  // 推挽输出模式
        GPIO_InitStruct.Pull = GPIO_PULLDOWN;  // 默认下拉
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;  // 速度设为高速(25 MHz to 50 MHz)
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);  // 初始化结构体变量
    
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);  //将 IO 拉低
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    delay_ms()

    delay_ms() 里运行的是 delay_us()delay_us()通过滴答定时器实现延时。上面的 delay_init() 已经将 fac_us 设置为了 80,滴答定时器计数 80 次需要用 10-6 秒(系统时钟为 80MHz),即 1us。

    /**
     * @brief	延时毫秒(ms)函数
     *
     * @param   nms		需要延时多少毫秒
     *
     * @return  void
     */
    void delay_ms(u16 nms)
    {
        u32 i;
    
        for(i = 0; i < nms; i++) delay_us(1000);
    }
    
    /**
     * @brief	延时微秒(us)函数
     *
     * @remark	nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)
     *
     * @param   nus		需要延时多少微秒
     *
     * @return  void
     */
    void delay_us(u32 nus)
    {
        u32 ticks;
        u32 told, tnow, tcnt = 0;
        u32 reload = SysTick->LOAD;				//LOAD的值
        ticks = nus * fac_us; 					//需要的节拍数
        told = SysTick->VAL;        			//刚进入时的计数器值
    
        while(1)
        {
            tnow = SysTick->VAL;
    
            if(tnow != told)
            {
                if(tnow < told)tcnt += told - tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
                else tcnt += reload - tnow + told;
    			
                told = tnow;
                if(tcnt >= ticks)break;			//时间超过/等于要延迟的时间,则退出.
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    BEEP()

    蜂鸣器的控制函数是宏函数,分别用到了 HAL_GPIO_WritePin()HAL_GPIO_TogglePin() 两个库函数。

    #define BEEP(n)	(n?HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_RESET))
    #define BEEP_TogglePin	HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_2)
    
    • 1
    • 2
  • 相关阅读:
    【数据结构】搜索二叉树(C++实现)
    MySQL数据库——DQL操作——基本查询
    NoveAI本地环境搭建、AI作画
    kubelet源码 删除pod(一)
    c++ 中 auto, auto & 和 const auto & 的区别
    VCS & Design Compiler(1)
    i.MX 6ULL 驱动开发 二十九:向 Linux 内核中添加自己编写驱动
    this用法,systemVerilog语法
    Mysql(五) MVCC机制
    12. 测试搭建百万并发项目
  • 原文地址:https://blog.csdn.net/weixin_43772810/article/details/125297857