• 学习STM32(2)--STM32单片机GPIO应用


    目录

    1  引 言 

    2  实验目的

    3  实验内容

    3.1掌握STM32F103的GPIO控制

    3.1.1 GPIO的分组

    3.1.2 GPIO的常用功能

    3.1.3 STM32单片机GPIO位结构

    3.1.4 STM32单片机GPIO工作模式

    3.1.5 STM32的GPIO 输出-点亮LED编程要点

    使用GPIO时,按下面步骤进行:

    3.2掌握基于while循环的按键检测程序

    3.2.1引脚定义

    3.2.2KEY输入配置

    3.3熟悉基于GPIO中断的按键检测程序

    NVIC 的主要功能包括:

    设置KEY1 按键中断所需的步骤:

    4 深入解析

    思考一(使用按键使得蜂鸣器发出声音)

    项目主要代码

    main.c

    bsp_beep.c

    bsp_beep.h

    bsp_key.c

     bsp_key.h

     思考二

    思考三(用蜂鸣器完成音乐播放)

    main.c

    bsp_beep.h

    bsp_beep.c


    引 言 

            在嵌入式系统中,GPIO(通用输入/输出)是一种重要的功能,它允许微控制器与外部设备进行通信和控制。STM32系列单片机作为一种广泛应用的嵌入式处理器,具有丰富的GPIO功能,可用于连接各种外部设备,如LED、按钮、传感器等。本次实验旨在探索STM32单片机的GPIO应用,通过学习如何配置GPIO引脚、读取和控制GPIO状态,以及使用GPIO实现简单的输入输出操作,从而深入理解STM32单片机的基本功能和应用。

    实验目的

    1. 掌握STM32F103开发板GPIO的输出功能
    2. 掌握STM32F103的GPIO输入,轮询式按键程序使用
    3. 熟悉STM32F103的中断按键检测程序

    实验内容

    3.1掌握STM32F103的GPIO控制

            GPIO(General Purpose Input Output)通用输入输出端口,STM32芯片的GPIO引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。掌握GPIO,就等于掌握了操作硬件的能力。

    3.1.1 GPIO的分组

            GPIOA-GPIOG,共7组,每组16个引脚,引脚号从0~15如PA0-PA15等,共112/144个。

            通过读写相关寄存器,实现对GPIO引脚的控制。

    3.1.2 GPIO的常用功能

            GPIO的常用功能普通输入和输出功能。

            输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等。

            输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等。

    3.1.3 STM32单片机GPIO位结构

            如下图1中STM32单片机GPIO位结构所示,从宏观到微观,从整体到细节观察,右边为IO引脚,左边为STM32F103的逻辑单元。上面为输入驱动,下面为输出驱动。输入,输出一定是站在单片机本身STM32F103的内核上来说的。

                                                      图1    STM32单片机GPIO位结构

    3.1.4 STM32单片机GPIO工作模式

    GPIO端口的每个位可以由软件分别配置成以下的工作模式:

    1)、输入 :浮空输入(Floating Input)(常用,在该模式下,引脚不连接到外部电路,处于高阻抗状态。可以通过读取引脚电平来检测外部信号。)

    2)、输入 :上拉输入(Pull-up Input)

    3)、输入 :下拉输入(Pull-down Input)

    4)、输入 :模拟输入(Analog Input)

    5)、输出: 推挽输出(Push-Pull Output)(常用,输出高低电平与电源电压基本没有压差,可以输出高电平或低电平,同时具有一定的驱动能力。引脚在输出低电平时形成低阻抗,输出高电平时形成高阻抗,可以驱动外部电路。)

    6)、输出:开漏输出(Open-Drain Output)

    7)、输出:复用推挽输出(AF Push-Pull Output)

    8)、输出:复用开漏输出(AF Open-Drain Output)

    3.1.5 STM32的GPIO 输出-点亮LED编程要点

    使用GPIO时,按下面步骤进行:

    1、配置系统时钟并打开GPIO口的时钟;

    2、设置GPIO口位的工作模式;

    3、使用GPIO口位进行输入或输出。

    为了使工程更加有条理,把 LED 灯控制相关的代码独立分开存储(.c和.h),方便以后移植。

    1. /*定义一个GPIO_InitTypeDef类型的结构体*/
    2. GPIO_InitTypeDef GPIO_InitStructure;

            使用 GPIO_InitTypeDef 定义 GPIO 初始化结构体变量,以便下面用于存储 GPIO 配置。

    1. /*开启LED相关的GPIO外设时钟*/
    2. RCC_APB2PeriphClockCmd( LED1_GPIO_CLK | LED2_GPIO_CLK | LED3_GPIO_CLK, ENABLE);

            调用库函数 RCC_APB2PeriphClockCmd 来使能 LED 灯的 GPIO 端口时钟,该函数有两个输入参数,第一个参数用于指示要配置的时钟,如本例中的“RCC_ APB2Periph_GPIOB”,应用时使用“|”操作同时配置 3 个 LED 灯的时钟;函数的第二个参数用于设置状态,可输入“Disable”关闭或“Enable”使能时钟。

    1. /*选择要控制的GPIO引脚*/
    2. GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
    3. /*设置引脚模式为通用推挽输出*/
    4. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  

            向 GPIO 初始化结构体赋值,把引脚初始化成推挽输出模式,其中的 GPIO_Pin 使用宏“LEDx_GPIO_PIN”来赋值,使函数的实现方便移植。

    1. /*设置引脚速率为50MHz */  
    2. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    3. /*调用库函数,初始化GPIO*/
    4. GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure); 

    使用以上初始化结构体的配置,调用 GPIO_Init 函数向寄存器写入参数,完成 GPIO 的初始化,这里的 GPIO 端口使用“LEDx_GPIO_PORT”宏来赋值,也是为了程序移植方便。

    1. /*选择要控制的GPIO引脚*/
    2. GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;
    3. /*调用库函数,初始化GPIO*/
    4. GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);

    使用同样的初始化结构体,只修改控制的引脚和端口,初始化其它 LED 灯使用的GPIO 引脚。

    3.2掌握基于while循环的按键检测程序

                                                              图2 Key_Scan函数

            定义了一个 Key_Scan 函数用于扫描按键状态。GPIO 引脚的输入电平可通过读取IDR寄存器对应的数据位来感知,而 STM32标准库提供了库函数GPIO_ReadInputDataBit 来获取位状态,该函数输入 GPIO 端口及引脚号,函数返回该引脚的电平状态,高电平返回 1,低电平返回 0。Key_Scan 函数中以 GPIO_ReadInputDataBit 的返回值与自定义的宏“KEY_ON”对比,若检测到按键按下,则使用 while 循环持续检测按键状态,直到按键释放,按键释放后 Key_Scan 函数返回一个“KEY_ON”值;若没有检测到按键按下,则函数直接返回“KEY_OFF”。若按键的硬件没有做消抖处理,需要在这个 Key_Scan 函数中做软件滤波,防止波纹抖动引起误触发。

            消抖是为了处理开关或按钮在按下或释放时产生的短暂不稳定信号。这种不稳定信号可能会导致系统错误地识别用户的操作。消抖的基本思路是在检测到按钮状态改变时,延时一小段时间,然后再次确认按钮状态,以确保信号稳定。

    3.2.1引脚定义

                                                            图3按键引脚定义

                                                            图4 LED引脚定义

    3.2.2KEY输入配置

                                                            图5 KEY输入配置 

    总结:对比输入和输出的配置步骤。基本上都包含了开启时钟、选择引脚、选择工作模式、选择速度。但是在输入模式下,无需配置引脚速度。

    3.3熟悉基于GPIO中断的按键检测程序

    EXTI_Key_Config();

    初始化EXTI中断,按下按键会触发中断,

    触发中断会进入stm32f4xx_it.c文件中的函数

    KEY1_IRQHandler和KEY2_IRQHandler,处理中断,反转LED灯。

                                                            图6 初始化EXTI函数 

    中断可以分为两种类型:可屏蔽中断(IRQ)和非可屏蔽中断(NMI)

    NVIC 的主要功能包括:

    中断优先级管理:每个中断都有一个相应的优先级,NVIC 允许程序员配置每个中断的优先级,以确保高优先级中断能够及时响应。

    中断使能与禁止:NVIC 可以控制每个中断的使能状态。当某个中断被禁用时,它将不会触发处理器的中断服务例程。

    中断状态查询:NVIC 允许程序查询每个中断的触发状态,以确定哪些中断正在等待处理。

    中断处理:当一个中断被触发时,NVIC 会根据中断优先级,中断向量表等信息来决定调用哪个中断服务例程(ISR,Interrupt Service Routine)来处理该中断。

    设置KEY1 按键中断所需的步骤:

    开启按键GPIO口的时钟:通过 RCC_APB2PeriphClockCmd开启按键GPIO口的时钟。

    配置NVIC中断:调用 NVIC_Configuration(); 配置 NVIC 中断。

    配置GPIO引脚:

    选择用于 KEY1 的 GPIO 引脚。

    将引脚配置为浮空输入,即不连接任何外部电路。这通常用于输入引脚,使其状态由外部设备决定。

    使用 GPIO_Init() 函数对 GPIO 进行配置。

    选择EXTI的信号源:通过 GPIO_EXTILineConfig选择 EXTI 的信号源,即选择外部中断线连接到哪个 GPIO 引脚上。

    配置EXTI中断:

    设置 EXTI_Line 为 KEY1 的外部中断线。

    将 EXTI 模式设置为中断模式。

    将触发模式设置为上升沿触发,即在按键按下时触发中断。

    使能中断线。

    4 深入解析

    思考一(使用按键使得蜂鸣器发出声音)

             1.日常电话在拨号过程中,按键的同时会有不同的按键声音。也就出现网络上流传的大学生通过按键声音破译了360董事长的手机号码。实验要求:基于STMF103单片机实现按键声音和按键检测功能,包含2个按键模拟数字,2个按键要采用不同的声音。

    ①初始化:

            配置单片机的GPIO引脚,将按键连接到输入引脚,将扬声器连接到输出引脚。

            初始化定时器,用于生成按键声音的方波。

    ②按键检测功能:

    循环执行以下步骤:

            读取按键的状态(按下或释放)。

    如果检测到按键按下:

            播放相应的按键声音。

    可以通过控制扬声器引脚的高低电平来实现声音的播放。

    ③按键声音的生成:

            当检测到按键按下时,根据不同的按键选择不同的频率和持续时间生成方波。

            可以使用定时器来定期改变输出引脚的状态,以产生方波。

            确保不同的按键对应不同的方波参数,以产生不同的声音效果。

    思考题一实现代码:

                                                          图7 主要的实现代码

    项目主要代码

    main.c
    1. #include "stm32f10x.h"
    2. #include "bsp_beep.h"
    3. #include "bsp_key.h"
    4. void SysTick_Delay_us( __IO uint32_t us) ;
    5. /**
    6. * @brief 主函数
    7. * @param 无
    8. * @retval 无
    9. */
    10. void Sound(u16 frq)
    11. {
    12. u32 time;
    13. if(frq != 1000)
    14. //if(frq != 1000):条件判断语句,判断音调的频率是否不等于 1000 Hz。
    15. {
    16. // time = 500000/((u32)frq);
    17. time = 100000/((u32)frq);
    18. //根据音调的频率计算延时时间。通常情况下,频率越高,延时时间越短,声音越高。
    19. // PBeep = 1;
    20. BEEP( ON );//打开蜂鸣器--根据自己的硬件情况调整,通常就是控制蜂鸣器的gpio引脚置1
    21. SysTick_Delay_us(time);
    22. // PBeep = 0;
    23. BEEP( OFF );//关闭蜂鸣器--根据自己的硬件情况调整,通常就是控制蜂鸣器的gpio引脚置0
    24. SysTick_Delay_us(time);
    25. }else
    26. SysTick_Delay_us(1000);
    27. //time = 100000/((u32)frq);:根据音调的频率计算延时时间。通常情况下,频率越高,延时时间越短,声音越高。
    28. }
    29. int main(void)
    30. {
    31. /* BEEP GPIO 初始化 */
    32. BEEP_GPIO_Config();
    33. Key_GPIO_Config();
    34. while(1)
    35. {
    36. u32 yanshi;
    37. u16 e;
    38. yanshi = 4;//10; 4; 2
    39. if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
    40. {
    41. for(e=0;e<(u16)(2*262/yanshi);e++){
    42. //在内部循环中,计算发声的次数,通过 (u16)time[i] * tone[music[i]] / yanshi 来确定。
    43. //这里将音符持续时间乘以音符对应的频率,再除以延时因子 yanshi,得到需要发声的次数。
    44. Sound(262);
    45. }
    46. }
    47. if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )
    48. {
    49. for(e=0;e<(u16)(2*262/yanshi);e++){
    50. //在内部循环中,计算发声的次数,通过 (u16)time[i] * tone[music[i]] / yanshi 来确定。
    51. //这里将音符持续时间乘以音符对应的频率,再除以延时因子 yanshi,得到需要发声的次数。
    52. Sound(262);
    53. }
    54. }
    55. }
    56. }
    57. void SysTick_Delay_us( __IO uint32_t us)
    58. {
    59. uint32_t i;
    60. SysTick_Config(SystemCoreClock/1000000);
    61. for (i=0; i<us; i++)
    62. {
    63. // 当计数器的值减小到 0 的时候,CRTL 寄存器的位 16 会置 1
    64. // 当置 1 时,读取该位会清 0
    65. while ( !((SysTick->CTRL)&(1<<16)) );
    66. }
    67. SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
    68. }
    69. /*********************************************END OF FILE**********************/
    bsp_beep.c
    1. #include "./beep/bsp_beep.h"
    2. /**
    3. * @brief 初始化控制蜂鸣器的IO
    4. * @param 无
    5. * @retval 无
    6. */
    7. void BEEP_GPIO_Config(void)
    8. {
    9. /*定义一个GPIO_InitTypeDef类型的结构体*/
    10. GPIO_InitTypeDef GPIO_InitStructure;
    11. /*开启控制蜂鸣器的GPIO的端口时钟*/
    12. RCC_APB2PeriphClockCmd( BEEP_GPIO_CLK, ENABLE);
    13. /*选择要控制蜂鸣器的GPIO*/
    14. GPIO_InitStructure.GPIO_Pin = BEEP_GPIO_PIN;
    15. /*设置GPIO模式为通用推挽输出*/
    16. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    17. /*设置GPIO速率为50MHz */
    18. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    19. /*调用库函数,初始化控制蜂鸣器的GPIO*/
    20. GPIO_Init(BEEP_GPIO_PORT, &GPIO_InitStructure);
    21. /* 关闭蜂鸣器*/
    22. GPIO_ResetBits(BEEP_GPIO_PORT, BEEP_GPIO_PIN);
    23. }
    24. /*********************************************END OF FILE**********************/
    bsp_beep.h
    1. #ifndef __BEEP_H
    2. #define __BEEP_H
    3. #include "stm32f10x.h"
    4. /* 定义蜂鸣器连接的GPIO端口, 用户只需要修改下面的代码即可改变控制的蜂鸣器引脚 */
    5. #define BEEP_GPIO_PORT GPIOC /* GPIO端口 */
    6. #define BEEP_GPIO_CLK RCC_APB2Periph_GPIOC /* GPIO端口时钟 */
    7. #define BEEP_GPIO_PIN GPIO_Pin_0 /* 连接到蜂鸣器的GPIO */
    8. /* 高电平时,蜂鸣器响 */
    9. #define ON 1
    10. #define OFF 0
    11. /* 带参宏,可以像内联函数一样使用 */
    12. #define BEEP(a) if (a) \
    13. GPIO_SetBits(BEEP_GPIO_PORT,BEEP_GPIO_PIN);\
    14. else \
    15. GPIO_ResetBits(BEEP_GPIO_PORT,BEEP_GPIO_PIN)
    16. void BEEP_GPIO_Config(void);
    17. #endif /* __BEEP_H */
    bsp_key.c
    1. #include "./key/bsp_key.h"
    2. /**
    3. * @brief 配置按键用到的I/O口
    4. * @param 无
    5. * @retval 无
    6. */
    7. void Key_GPIO_Config(void)
    8. {
    9. GPIO_InitTypeDef GPIO_InitStructure;
    10. /*开启按键端口的时钟*/
    11. RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK,ENABLE);
    12. //选择按键的引脚
    13. GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;
    14. // 设置按键的引脚为浮空输入
    15. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    16. //使用结构体初始化按键
    17. GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);
    18. //选择按键的引脚
    19. GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN;
    20. //设置按键的引脚为浮空输入
    21. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    22. //使用结构体初始化按键
    23. GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);
    24. }
    25. /*
    26. * 函数名:Key_Scan
    27. * 描述 :检测是否有按键按下
    28. * 输入 :GPIOx:x 可以是 A,B,C,D或者 E
    29. * GPIO_Pin:待读取的端口位
    30. * 输出 :KEY_OFF(没按下按键)、KEY_ON(按下按键)
    31. */
    32. uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
    33. {
    34. /*检测是否有按键按下 */
    35. if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON )
    36. {
    37. /*等待按键释放 */
    38. while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON);
    39. return KEY_ON;
    40. }
    41. else
    42. return KEY_OFF;
    43. }
    44. /*********************************************END OF FILE**********************/
     bsp_key.h
    1. #ifndef __KEY_H
    2. #define __KEY_H
    3. #include "stm32f10x.h"
    4. // 引脚定义
    5. #define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA
    6. #define KEY1_GPIO_PORT GPIOA
    7. #define KEY1_GPIO_PIN GPIO_Pin_0
    8. #define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC
    9. #define KEY2_GPIO_PORT GPIOC
    10. #define KEY2_GPIO_PIN GPIO_Pin_13
    11. /** 按键按下标置宏
    12. * 按键按下为高电平,设置 KEY_ON=1KEY_OFF=0
    13. * 若按键按下为低电平,把宏设置成KEY_ON=0KEY_OFF=1 即可
    14. */
    15. #define KEY_ON 1
    16. #define KEY_OFF 0
    17. void Key_GPIO_Config(void);
    18. uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
    19. #endif /* __KEY_H */

     思考二

    2.在上述的电话按键系统设计过程中,如何设计可以避免通过按键声音破译手机号码(要求声音保留)。

            1.随机化声音生成:每次按下按键时,不要产生固定频率和持续时间的声音。而是在一定的范围内随机选择频率和持续时间,以使得声音不易被识别和破解。

            2.将所有按键的声音都改为同一声音

                                                       图8 思考题2实现代码 

    思考三(用蜂鸣器完成音乐播放)

    详细内容看博客:http://t.csdnimg.cn/V0uRD

    main.c

    1. #include "stm32f10x.h"
    2. #include "./beep/bsp_beep.h"
    3. void Buzzer_On(void)
    4. {
    5. GPIO_SetBits(BEEP_GPIO_PORT, BEEP_GPIO_PIN);
    6. }
    7. void Buzzer_Off(void)
    8. {
    9. GPIO_ResetBits(BEEP_GPIO_PORT, BEEP_GPIO_PIN);
    10. }
    11. void delay_us (const uint32_t usec)
    12. {
    13. RCC_ClocksTypeDef RCC_Clocks;
    14. /* Configure HCLK clock as SysTick clock source */
    15. SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
    16. RCC_GetClocksFreq(&RCC_Clocks);
    17. // Set SysTick Reload(1us) register and Enable
    18. // usec * (RCC_Clocks.HCLK_Frequency / 1000000) < 0xFFFFFFUL -- because of 24bit timer
    19. SysTick_Config(usec * (RCC_Clocks.HCLK_Frequency / 1000000));//HCLK_Frequency=48M
    20. // 72/72000000 --> 1usec
    21. // 0.001msec = 1usec
    22. // 1Hz = 1sec, 10Hz = 100msec, 100Hz = 10msec, 1KHz = 1msec,
    23. // 10KHz = 0.1msec, 100Khz = 0.01msec, 1MHz = 1usec(0.001msec)
    24. // 1usec = 1MHz
    25. // SysTick Interrupt Disable
    26. SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk ;
    27. // Until Tick count is 0
    28. while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
    29. }
    30. void BEEP_Init(void)
    31. {
    32. /*定义一个GPIO_InitTypeDef类型的结构体*/
    33. GPIO_InitTypeDef GPIO_InitStructure;
    34. /*开启控制蜂鸣器的GPIO的端口时钟*/
    35. RCC_APB2PeriphClockCmd( BEEP_GPIO_CLK, ENABLE);
    36. /*选择要控制蜂鸣器的GPIO*/
    37. GPIO_InitStructure.GPIO_Pin = BEEP_GPIO_PIN;
    38. /*设置GPIO模式为通用推挽输出*/
    39. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    40. /*设置GPIO速率为50MHz */
    41. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    42. /*调用库函数,初始化控制蜂鸣器的GPIO*/
    43. GPIO_Init(BEEP_GPIO_PORT, &GPIO_InitStructure);
    44. /* 关闭蜂鸣器*/
    45. GPIO_ResetBits(BEEP_GPIO_PORT, BEEP_GPIO_PIN);
    46. }
    47. void Sound(u16 frq)
    48. {
    49. u32 time;
    50. if(frq != 1000)
    51. //if(frq != 1000):条件判断语句,判断音调的频率是否不等于 1000 Hz。
    52. {
    53. // time = 500000/((u32)frq);
    54. time = 100000/((u32)frq);
    55. //根据音调的频率计算延时时间。通常情况下,频率越高,延时时间越短,声音越高。
    56. // PBeep = 1;
    57. Buzzer_On();//打开蜂鸣器--根据自己的硬件情况调整,通常就是控制蜂鸣器的gpio引脚置1
    58. delay_us(time*2);
    59. // PBeep = 0;
    60. Buzzer_Off();//关闭蜂鸣器--根据自己的硬件情况调整,通常就是控制蜂鸣器的gpio引脚置0
    61. delay_us(time*2);
    62. }else
    63. delay_us(1000);
    64. //time = 100000/((u32)frq);:根据音调的频率计算延时时间。通常情况下,频率越高,延时时间越短,声音越高。
    65. }
    66. void play_music(void)
    67. {
    68. //7 1 2 3 4 5 6 712345 不发音
    69. uc16 tone[] = {247,262,294,330,349,392,440,494,523,587,659,698,784,1000};//音频数据表
    70. //小燕子
    71. u8 music[]={3,5,8,6,5,13,//音调
    72. 3,5,6,8,5,13,
    73. 8,10,9,8,9,8,6,8,5,13,
    74. 3,5,6,5,6,8,9,5,6,13,
    75. 3,2,1,2,13,
    76. 2,2,3,5,5,8,2,3,5,13};
    77. u8 time[] ={2,2,2,2,6,4,//时间
    78. 2,2,2,2,6,4,
    79. 6,2,4,4,2,2,2,2,6,4,
    80. 6,2,4,2,2,4,2,2,6,4,
    81. 2,2,4,6,4,
    82. 4,2,2,4,4,4,2,2,6,4};
    83. // u8 music[]={13,1,2,3,4,5,6,7,8};//测试基础音
    84. // u8 time[] ={4, 4,4,4,4,4,4,4,4};
    85. u32 yanshi;
    86. u16 i,e;
    87. yanshi = 4;//10; 4; 2
    88. //实际播放的音调和持续时间会受到延时因子yanshi的影响,通过调节yanshi的值可以控制播放速度。
    89. for(i=0;i<sizeof(music)/sizeof(music[0]);i++){
    90. for(e=0;e<((u16)time[i])*tone[music[i]]/yanshi;e++){
    91. //在内部循环中,计算发声的次数,通过 (u16)time[i] * tone[music[i]] / yanshi 来确定。
    92. //这里将音符持续时间乘以音符对应的频率,再除以延时因子 yanshi,得到需要发声的次数。
    93. Sound((u32)tone[music[i]]);
    94. }
    95. }
    96. }
    97. int main(void)
    98. {
    99. BEEP_Init();
    100. while(1)
    101. {
    102. play_music();
    103. }
    104. return 0;
    105. }

    bsp_beep.h

    1. #ifndef __BEEP_H
    2. #define __BEEP_H
    3. #include "stm32f10x.h"
    4. /* 定义蜂鸣器连接的GPIO端口, 用户只需要修改下面的代码即可改变控制的蜂鸣器引脚 */
    5. #define BEEP_GPIO_PORT GPIOC /* GPIO端口 */
    6. #define BEEP_GPIO_CLK RCC_APB2Periph_GPIOC /* GPIO端口时钟 */
    7. #define BEEP_GPIO_PIN GPIO_Pin_0 /* 连接到蜂鸣器的GPIO */
    8. /* 高电平时,蜂鸣器响 */
    9. #define ON 1
    10. #define OFF 0
    11. /* 带参宏,可以像内联函数一样使用 */
    12. #define BEEP(a) if (a) \
    13. GPIO_SetBits(BEEP_GPIO_PORT,BEEP_GPIO_PIN);\
    14. else \
    15. GPIO_ResetBits(BEEP_GPIO_PORT,BEEP_GPIO_PIN)
    16. void BEEP_GPIO_Config(void);
    17. #endif /* __BEEP_H */

    bsp_beep.c

    1. #include "./beep/bsp_beep.h"
    2. /**
    3. * @brief 初始化控制蜂鸣器的IO
    4. * @param 无
    5. * @retval 无
    6. */
    7. void BEEP_GPIO_Config(void)
    8. {
    9. /*定义一个GPIO_InitTypeDef类型的结构体*/
    10. GPIO_InitTypeDef GPIO_InitStructure;
    11. /*开启控制蜂鸣器的GPIO的端口时钟*/
    12. RCC_APB2PeriphClockCmd( BEEP_GPIO_CLK, ENABLE);
    13. /*选择要控制蜂鸣器的GPIO*/
    14. GPIO_InitStructure.GPIO_Pin = BEEP_GPIO_PIN;
    15. /*设置GPIO模式为通用推挽输出*/
    16. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    17. /*设置GPIO速率为50MHz */
    18. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    19. /*调用库函数,初始化控制蜂鸣器的GPIO*/
    20. GPIO_Init(BEEP_GPIO_PORT, &GPIO_InitStructure);
    21. /* 关闭蜂鸣器*/
    22. GPIO_ResetBits(BEEP_GPIO_PORT, BEEP_GPIO_PIN);
    23. }
    24. /*********************************************END OF FILE**********************/

  • 相关阅读:
    数据分析与取证capture.pcapng
    MySQL数据库管理
    反编译之崩溃定位
    我的创作纪念日--AI小怪兽打怪进阶路
    Java Reflection构造器的示例简介说明
    vscode自定义代码提示
    Unity DOTS学习 前置知识(一)
    simulink代码生成
    ELK日志分析系统实战
    Docker的初级使用
  • 原文地址:https://blog.csdn.net/weixin_73690807/article/details/140914899