zoukankan      html  css  js  c++  java
  • 利用STM32播放音乐

    使用STM32F411CC,接上一个无源蜂鸣器,播放音乐。

    无源蜂鸣器需要外部输入PWM信号驱动蜂鸣器振动发声。

    (1)蜂鸣器接在PB10上。

    (2)使用定时器2控制输出PWM。

    第一次构思的版本:

      1 // 音频参数结构体
      2 // 周期:每个音符都有自己的周期,参考https://wenku.baidu.com/view/4d69b296dd88d0d233d46a80.html
      3 // 节拍:大概就是每个音符持续多长的时间
      4 struct tone_play_data {
      5     uint16_t    period;
      6     uint16_t    duration;
      7 };
      8 
      9 struct tone_play_ctrl {
     10     uint32_t    arrCount;
     11     const struct tone_play_data *toneParam;
     12 };
     13 
     14 // 定义一组测试使用的音符
     15 // 本来想用另外一个硬件定时器来专门控制节拍时长,以便达到精确控制
     16 // 后来想了想就没有这么做了
     17 // 现在只采用一个硬件定时器来控制节拍的时长,当让不很精确,不过也差不太远吧
     18 #define COUNT_10MS(N) ((N)*10)
     19 const struct tone_play_data testTone[] = {
     20     {1912, COUNT_10MS(200)},
     21     {1703, COUNT_10MS(200)},
     22     {1517, COUNT_10MS(200)},
     23     {1432, COUNT_10MS(200)},
     24     {1275, COUNT_10MS(200)},
     25     {1136, COUNT_10MS(200)},
     26     {1012, COUNT_10MS(200)},
     27     {955, COUNT_10MS(200)},
     28     {851, COUNT_10MS(200)},
     29     {758, COUNT_10MS(200)},
     30     {751, COUNT_10MS(200)},
     31     {637, COUNT_10MS(200)},
     32     {568, COUNT_10MS(200)},
     33     {508, COUNT_10MS(200)},
     34     {3816, COUNT_10MS(200)},
     35     
     36     // 注意不要忘记最后的这组元素,用来控制结束播放的
     37     {0, 0},
     38 };
     39 
     40 // 播放控制变量,每次启动播放前必须初始化
     41 static struct tone_play_ctrl mController = {
     42     .arrCount = 0,
     43     .toneParam = &testTone[0],
     44 };
     45 
     46 // 定时器中断回调,要是硬件自己处理该多好啊,这样的效率有点低
     47 // 定时器1&8有个RCR(repetition counter register),也许应该换定时器1&8的
     48 extern void TIM2_IRQHandler(void)
     49 {
     50     if (TIM_GetFlagStatus(TIM2, TIM_IT_Update))
     51     {
     52         // 进来一次说明经历了ARR时长
     53         mController.arrCount ++;
     54         
     55         // duration单位是ms,ARR经过我们的配置后是1us,所以比较时要对duration乘以1000
     56         // 这里的音符的时长精度肯定有较大的误差,但是咱们暂不考虑处理这个
     57         if ((TIM2->ARR + 1) * mController.arrCount >= mController.toneParam->duration * 1000)
     58         {
     59             mController.toneParam++;
     60             if (0 < mController.toneParam->period)
     61             {
     62                 // 音符切换,必须重置arrCount,重设定时器的ARR寄存器和PWM的CCR寄存器
     63                 mController.arrCount = 0;
     64                 TIM_SetAutoreload(TIM2, mController.toneParam->period - 1);
     65                 TIM_SetCompare3(TIM2, (mController.toneParam->period + 1) / 2);
     66             }
     67             else
     68             {
     69                 // 播放完毕了就关闭定时器
     70                 TIM_Cmd(TIM2, DISABLE);
     71             }
     72         }
     73         
     74         TIM_ClearFlag(TIM2, TIM_IT_Update);
     75     }
     76 }
     77 
     78 /*
     79 STM32F411CC使用的外部晶振16M,所得到的sysclk为100MHZ
     80 */
     81 void environment_setup(void)
     82 {
     83     /* TIMER2, CHANNEL 3, PB10 */
     84     GPIO_InitTypeDef gpioInitData = {
     85         GPIO_Pin_10, 
     86         GPIO_Mode_AF,
     87         GPIO_Fast_Speed,
     88         GPIO_OType_PP,
     89         GPIO_PuPd_UP,
     90     };
     91     TIM_TimeBaseInitTypeDef timeData = {
     92         .TIM_CounterMode = TIM_CounterMode_Up,
     93         .TIM_ClockDivision = TIM_CKD_DIV1,
     94     };
     95     TIM_OCInitTypeDef ocdata = {
     96         .TIM_OCMode = TIM_OCMode_PWM2, // PWM模式2:CNT>CCR时输出有效
     97         .TIM_OutputState = TIM_OutputState_Enable,
     98         .TIM_OutputNState = TIM_OutputNState_Enable, // 仅Timer1&8, PWM模式用不上这个
     99         .TIM_OCPolarity = TIM_OCPolarity_High,
    100         .TIM_OCNPolarity = TIM_OCNPolarity_Low, // PWM模式用不上这个
    101         .TIM_OCIdleState = TIM_OCIdleState_Set, // PWM模式用不上这个
    102         .TIM_OCNIdleState = TIM_OCNIdleState_Reset, // PWM模式用不上这个
    103     };
    104 
    105     /* 定时器溢出周期,
    106        系统主频100MHz不分频(TIM_ClockDivision=TIM_CKD_DIV1)直接给到定时器2作为计数时钟,
    107        计数时钟信号100倍分频(TIM_Prescaler=100-1)就是1MHz,一个时钟周期就是1us。
    108        所以对计数时钟信号计数n个(TIM_Prescaler=n-1)就达到了n微秒。
    109        注意,我们后面主要控制 TIM_Prescaler 来达到音乐频率的控制。
    110        这里TIM_Prescaler=99是固定配置,一个计数时钟周期为1us,
    111        如果你使用的是72MHz(比如STM32F103),或者TIM_ClockDivision做了分频,要注意更改。
    112     */
    113     timeData.TIM_Prescaler = 100 - 1; 
    114     timeData.TIM_Period = toneParam->period - 1;
    115     // PWM占空比50%,就是 TIM_Period 的一半
    116     ocdata.TIM_Pulse = (timeData.TIM_Period + 1) / 2;
    117 
    118     // 我们要使用中断
    119     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    120     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    121     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    122 
    123     // 初始化GPIO以及功能复用
    124     GPIO_Init(GPIOB, &gpioInitData);
    125     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_TIM2);
    126 
    127     // 定时器中断分配
    128     NVIC_InitTypeDef nvicInitData;
    129     nvicInitData.NVIC_IRQChannel = TIM2_IRQn;
    130     nvicInitData.NVIC_IRQChannelPreemptionPriority = 2;
    131     nvicInitData.NVIC_IRQChannelSubPriority = 2;
    132     nvicInitData.NVIC_IRQChannelCmd = ENABLE;
    133     NVIC_Init(&nvicInitData);
    134     
    135     TIM_DeInit(TIM2);
    136     TIM_TimeBaseInit(TIM2, &timeData);
    137     TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 启用中断
    138     TIM_OC3Init(TIM2, &ocdata); //TIM2的通道2PWM 模式设置
    139     TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能预装载寄存器
    140     TIM_Cmd(TIM2, ENABLE); // 启动定时器
    141 }
    142 
    143 /*
    144 接口函数,注意形参传值的正确性
    145 */
    146 void tone_play(const struct tone_play_data *param)
    147 {
    148     TIM_Cmd(TIM2, DISABLE);
    149     if (NULL == param)
    150         return;
    151     mController.arrCount = 0;
    152     mController.toneParam = param;
    153     TIM_SetAutoreload(TIM2, mController.toneParam->period - 1);
    154     TIM_SetCompare3(TIM2, (mController.toneParam->period + 1) / 2);
    155     TIM_Cmd(TIM2, ENABLE);    
    156 }
    157 
    158 void tone_stop(void)
    159 {
    160     TIM_Cmd(TIM2, DISABLE);
    161 }
    View Code

    第二次构思的版本:

    上面的代码还可以再继续优化一下:

    (1)每个音符的频率都是固定的,我们可以弄一个固定的音符表格。

    (2)我们设定一个变量,用来设定一个节拍的周期,这样对于1/4,1/8之类的节拍就只要对应的乘除就好。

    (3)比上面那种处理方式要节约空间。

      1 // 最新编辑:2020/9/24
      2 typedef enum {
      3     TONE_END = 0,
      4     // 好像有个不发音的?保持1KHz的频率
      5     TONE_MUTE,
      6     TONE_LOW_1,
      7     TONE_LOW_2,
      8     TONE_LOW_3,
      9     TONE_LOW_4,
     10     TONE_LOW_5,
     11     TONE_LOW_6,
     12     TONE_LOW_7,
     13     TONE_MIDDLE_1,
     14     TONE_MIDDLE_2,
     15     TONE_MIDDLE_3,
     16     TONE_MIDDLE_4,
     17     TONE_MIDDLE_5,
     18     TONE_MIDDLE_6,
     19     TONE_MIDDLE_7,
     20     TONE_HIGH_1,
     21     TONE_HIGH_2,
     22     TONE_HIGH_3,
     23     TONE_HIGH_4,
     24     TONE_HIGH_5,
     25     TONE_HIGH_6,
     26     TONE_HIGH_7,
     27     // 将你的音符索引添加到这一行的上面
     28     TONE_TOTAL
     29 } ToneSymbIdx; 
     30 
     31 #define TONE_SYMBOL_END_PERIOD     (0)
     32 #define TONE_SYMBOL_MUTE_PERIOD    (1000)
     33 const uint16_t toneSymbols[TONE_TOTAL] = {
     34     TONE_SYMBOL_END_PERIOD,
     35     TONE_SYMBOL_MUTE_PERIOD,
     36     // 低音12345657
     37     3816, 3401, 3030, 2865, 2551, 2272, 2024,
     38     // 中音12345657
     39     1912, 1703, 1517, 1432, 1275, 1136, 1012,
     40     // 高音12345657
     41     955,  851,  758,  751,  637,  568,  508,
     42 };
     43 // 音乐节拍主要分为:
     44 // 1/4,2/4,3/4,4/4
     45 const uint32_t toneBeatBaseUS = 128000;
     46 #define TONE_BEAT_1_4_MULTIPLIER   (1)
     47 #define TONE_BEAT_2_4_MULTIPLIER   (2)
     48 #define TONE_BEAT_3_4_MULTIPLIER   (4)
     49 #define TONE_BEAT_4_4_MULTIPLIER   (8)
     50 
     51 struct tone_play_data {
     52     ToneSymbIdx symbolIndex;
     53     uint8_t beatMultiple;
     54 };
     55 
     56 struct tone_play_ctrl {
     57     uint32_t    arrCount;
     58     const struct tone_play_data *toneParam;
     59 };
     60 
     61 // 相比前面的设计,这样一首歌就可以节省很多空间出来了
     62 const struct tone_play_data testTone[] = {
     63     {TONE_LOW_1,    TONE_BEAT_1_4_MULTIPLIER},
     64     {TONE_LOW_2,    TONE_BEAT_2_4_MULTIPLIER},
     65     {TONE_LOW_3,    TONE_BEAT_3_4_MULTIPLIER},
     66     {TONE_LOW_4,    TONE_BEAT_4_4_MULTIPLIER},
     67     {TONE_LOW_5,    TONE_BEAT_1_4_MULTIPLIER},
     68     {TONE_LOW_6,    TONE_BEAT_2_4_MULTIPLIER},
     69     {TONE_LOW_7,    TONE_BEAT_3_4_MULTIPLIER},
     70     {TONE_MUTE,     TONE_BEAT_1_4_MULTIPLIER},
     71     {TONE_MIDDLE_1, TONE_BEAT_1_4_MULTIPLIER},
     72     {TONE_MIDDLE_2, TONE_BEAT_2_4_MULTIPLIER},
     73     {TONE_MIDDLE_3, TONE_BEAT_3_4_MULTIPLIER},
     74     {TONE_MIDDLE_4, TONE_BEAT_4_4_MULTIPLIER},
     75     {TONE_MIDDLE_5, TONE_BEAT_1_4_MULTIPLIER},
     76     {TONE_MIDDLE_6, TONE_BEAT_2_4_MULTIPLIER},
     77     {TONE_MIDDLE_7, TONE_BEAT_3_4_MULTIPLIER},
     78     {TONE_MUTE,     TONE_BEAT_1_4_MULTIPLIER},
     79     {TONE_HIGH_1,   TONE_BEAT_1_4_MULTIPLIER},
     80     {TONE_HIGH_2,   TONE_BEAT_2_4_MULTIPLIER},
     81     {TONE_HIGH_3,   TONE_BEAT_3_4_MULTIPLIER},
     82     {TONE_HIGH_4,   TONE_BEAT_4_4_MULTIPLIER},
     83     {TONE_HIGH_5,   TONE_BEAT_1_4_MULTIPLIER},
     84     {TONE_HIGH_6,   TONE_BEAT_3_4_MULTIPLIER},
     85     {TONE_HIGH_7,   TONE_BEAT_4_4_MULTIPLIER},
     86 
     87     // 最后一个元素必须是{0, 0}
     88     {TONE_END, 0},
     89 };
     90 
     91 // 播放控制变量,每次启动播放前必须初始化
     92 static struct tone_play_ctrl mController;
     93 
     94 // 定时器中断回调,要是硬件自己处理该多好啊,这样的效率有点低
     95 // 定时器1&8有个RCR(repetition counter register),也许应该换定时器1&8的
     96 extern void TIM2_IRQHandler(void)
     97 {
     98     if (TIM_GetFlagStatus(TIM2, TIM_IT_Update))
     99     {
    100         // 进来一次说明经历了ARR时长
    101         mController.arrCount ++;
    102         
    103         // duration单位是ms,ARR经过我们的配置后是1us,所以比较时要对duration乘以1000
    104         // 这里的音符的时长精度肯定有较大的误差,但是咱们暂不考虑处理这个
    105         if ((TIM2->ARR + 1) * mController.arrCount >= mController.toneParam->beatMultiple * toneBeatBaseUS)
    106         {
    107             mController.toneParam++;
    108             if (0 < mController.toneParam->beatMultiple && TONE_END < mController.toneParam->symbolIndex)
    109             {
    110                 // 音符切换,必须重置arrCount,重设定时器的ARR寄存器和PWM的CCR寄存器
    111                 mController.arrCount = 0;
    112                 TIM_SetAutoreload(TIM2, toneSymbols[mController.toneParam->symbolIndex] - 1);
    113                 
    114                 // 如果是静音音符,直接控制PWM周期与节拍周期一致就能达到静音了
    115                 if (TONE_MUTE == mController.toneParam->symbolIndex)
    116                     TIM_SetCompare3(TIM2, toneSymbols[mController.toneParam->symbolIndex]);
    117                 else
    118                     TIM_SetCompare3(TIM2, (toneSymbols[mController.toneParam->symbolIndex] + 1) / 2);
    119             }
    120             else
    121             {
    122                 // 播放完毕了就关闭定时器
    123                 TIM_Cmd(TIM2, DISABLE);
    124                 TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable);
    125             }
    126         }
    127         
    128         TIM_ClearFlag(TIM2, TIM_IT_Update);
    129     }
    130 }
    131 
    132 /*
    133 STM32F411CC使用的外部晶振16M,所得到的sysclk为100MHZ
    134 */
    135 void environment_setup(void)
    136 {
    137     /* TIMER2, CHANNEL 3, PB10 */
    138     GPIO_InitTypeDef gpioInitData = {
    139         GPIO_Pin_10, 
    140         GPIO_Mode_AF,
    141         GPIO_Fast_Speed,
    142         GPIO_OType_PP,
    143         GPIO_PuPd_UP,
    144     };
    145     TIM_TimeBaseInitTypeDef timeData = {
    146         .TIM_CounterMode = TIM_CounterMode_Up,
    147         .TIM_ClockDivision = TIM_CKD_DIV1,
    148     };
    149     TIM_OCInitTypeDef ocdata = {
    150         .TIM_OCMode = TIM_OCMode_PWM2, // PWM模式2:CNT>CCR时输出有效
    151         .TIM_OutputState = TIM_OutputState_Enable,
    152         .TIM_OutputNState = TIM_OutputNState_Enable, // 仅Timer1&8, PWM模式用不上这个
    153         .TIM_OCPolarity = TIM_OCPolarity_High,
    154         .TIM_OCNPolarity = TIM_OCNPolarity_Low, // PWM模式用不上这个
    155         .TIM_OCIdleState = TIM_OCIdleState_Set, // PWM模式用不上这个
    156         .TIM_OCNIdleState = TIM_OCNIdleState_Reset, // PWM模式用不上这个
    157     };
    158 
    159     /* 定时器溢出周期,
    160        系统主频100MHz不分频(TIM_ClockDivision=TIM_CKD_DIV1)直接给到定时器2作为计数时钟,
    161        计数时钟信号100倍分频(TIM_Prescaler=100-1)就是1MHz,一个时钟周期就是1us。
    162        所以对计数时钟信号计数n个(TIM_Prescaler=n-1)就达到了n微秒。
    163        注意,我们后面主要控制 TIM_Prescaler 来达到音乐频率的控制。
    164        这里TIM_Prescaler=99是固定配置,一个计数时钟周期为1us,
    165        如果你使用的是72MHz(比如STM32F103),或者TIM_ClockDivision做了分频,要注意更改。
    166     */
    167     timeData.TIM_Prescaler = 100 - 1; // 这个值为99,固定不动,如果是72M,则改成71
    168     timeData.TIM_Period = 1000 - 1; // 这个999是1ms,后面会动态更改,这里随便写
    169     // PWM占空比50%,就是 TIM_Period 的一半
    170     ocdata.TIM_Pulse = 500;
    171 
    172     // 我们要使用中断
    173     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    174     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    175     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    176 
    177     // 初始化GPIO以及功能复用
    178     GPIO_Init(GPIOB, &gpioInitData);
    179     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_TIM2);
    180 
    181     // 定时器中断分配
    182     NVIC_InitTypeDef nvicInitData;
    183     nvicInitData.NVIC_IRQChannel = TIM2_IRQn;
    184     nvicInitData.NVIC_IRQChannelPreemptionPriority = 2;
    185     nvicInitData.NVIC_IRQChannelSubPriority = 2;
    186     nvicInitData.NVIC_IRQChannelCmd = ENABLE;
    187     NVIC_Init(&nvicInitData);
    188     
    189     TIM_DeInit(TIM2);
    190     TIM_TimeBaseInit(TIM2, &timeData);
    191     TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 启用中断
    192     TIM_OC3Init(TIM2, &ocdata); //TIM2的通道2PWM 模式设置
    193     TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable); //使能预装载寄存器
    194     TIM_Cmd(TIM2, DISABLE); // 启动定时器
    195 }
    196 
    197 /*
    198 接口函数,注意形参传值的正确性
    199 */
    200 void tone_play(const struct tone_play_data *param)
    201 {
    202     if (NULL == param)
    203         return;
    204     // 如果你有RTOS,这里要注意使用互斥量,保证线程安全
    205     TIM_Cmd(TIM2, DISABLE);
    206     mController.arrCount = 0;
    207     mController.toneParam = param;
    208     TIM_SetAutoreload(TIM2, toneSymbols[mController.toneParam->symbolIndex] - 1);
    209     TIM_SetCompare3(TIM2, (toneSymbols[mController.toneParam->symbolIndex] + 1) / 2);
    210     TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
    211     TIM_Cmd(TIM2, ENABLE);
    212 }
    213 
    214 void tone_stop(void)
    215 {
    216     // 如果你有RTOS,这里要注意使用互斥量,保证线程安全
    217     TIM_Cmd(TIM2, DISABLE);
    218     TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable);
    219 }
    View Code

    最终版本(源码H和C文件都贴上来了,复制黏贴后就能使用):

      1 // H文件内容
      2 #if defined(__BEEP_MUSIC_SUPPORT__)
      3 typedef enum tonesymbol {
      4     TONE_SYMB_END = 0,
      5     // 好像有个不发音的?保持1KHz的频率
      6     TONE_SYMB_MUTE,
      7     TONE_SYMB_LOW_1,
      8     TONE_SYMB_LOW_1H,
      9     TONE_SYMB_LOW_2,
     10     TONE_SYMB_LOW_2H,
     11     TONE_SYMB_LOW_3,
     12     TONE_SYMB_LOW_3H,
     13     TONE_SYMB_LOW_4,
     14     TONE_SYMB_LOW_4H,
     15     TONE_SYMB_LOW_5,
     16     TONE_SYMB_LOW_5H,
     17     TONE_SYMB_LOW_6,
     18     TONE_SYMB_LOW_6H,
     19     TONE_SYMB_LOW_7,
     20     TONE_SYMB_LOW_7H,
     21     TONE_SYMB_MIDDLE_1,
     22     TONE_SYMB_MIDDLE_1H,
     23     TONE_SYMB_MIDDLE_2,
     24     TONE_SYMB_MIDDLE_2H,
     25     TONE_SYMB_MIDDLE_3,
     26     TONE_SYMB_MIDDLE_3H,
     27     TONE_SYMB_MIDDLE_4,
     28     TONE_SYMB_MIDDLE_4H,
     29     TONE_SYMB_MIDDLE_5,
     30     TONE_SYMB_MIDDLE_5H,
     31     TONE_SYMB_MIDDLE_6,
     32     TONE_SYMB_MIDDLE_6H,
     33     TONE_SYMB_MIDDLE_7,
     34     TONE_SYMB_MIDDLE_7H,
     35     TONE_SYMB_HIGH_1,
     36     TONE_SYMB_HIGH_1H,
     37     TONE_SYMB_HIGH_2,
     38     TONE_SYMB_HIGH_2H,
     39     TONE_SYMB_HIGH_3,
     40     TONE_SYMB_HIGH_3H,
     41     TONE_SYMB_HIGH_4,
     42     TONE_SYMB_HIGH_4H,
     43     TONE_SYMB_HIGH_5,
     44     TONE_SYMB_HIGH_5H,
     45     TONE_SYMB_HIGH_6,
     46     TONE_SYMB_HIGH_6H,
     47     TONE_SYMB_HIGH_7,
     48     TONE_SYMB_HIGH_7H,
     49     // 将你的音符索引添加到这一行的上面
     50     TONE_TOTAL
     51 } ToneSymbIdx; 
     52 
     53 typedef enum tonebeat {
     54     TONE_BEAT_END = 0,
     55     TONE_BEAT_4_1, // 4/1拍
     56     TONE_BEAT_3_1, // 3/1拍
     57     TONE_BEAT_2_1, // 2/1拍
     58     TONE_BEAT_3_2, // 3/2拍
     59     TONE_BEAT_1_1, // 1/1拍
     60     TONE_BEAT_3_4, // 3/4拍
     61     TONE_BEAT_1_2, // 1/2拍
     62     TONE_BEAT_1_3, // 1/3拍
     63     TONE_BEAT_1_4, // 1/4拍
     64 
     65     TONE_BEAT_TOTAL,
     66 } ToneBeatIdx;
     67 
     68 struct tone_play_data {
     69     ToneSymbIdx symbolIndex;
     70     ToneBeatIdx beatIndex;
     71 };
     72 
     73 extern const struct tone_play_data beep_Du[];
     74 /*
     75 // 定义自己的音乐曲谱
     76 // const struct tone_play_data beep_Du[] = {
     77 //     {TONE_SYMB_HIGH_1, TONE_BEAT_1_2},
     78 //     // 最后一个元素必须是{0, 0}
     79 //     {TONE_SYMB_END, TONE_BEAT_END},
     80 // };
     81 */
     82 
     83 /*
     84 接口函数,注意形参传值的正确性
     85 */
     86 extern void tone_play(const struct tone_play_data *param);
     87 extern void tone_stop(void);
     88 #endif
     89 
     90 // C文件内容
     91 #if defined(__BEEP_MUSIC_SUPPORT__) // TONE PLAY
     92 // 每个音符的周期
     93 #define TONE_SYMBOL_END_PERIOD     (0)
     94 #define TONE_SYMBOL_MUTE_PERIOD    (10000)
     95 static const uint16_t toneSymbolPeriod[TONE_TOTAL] = {
     96     #ifdef ADD_SYMB_VALUE
     97     #undef ADD_SYMB_VALUE
     98     #endif
     99     #define ADD_SYMB_VALUE(E,V) (V)
    100     ADD_SYMB_VALUE(TONE_SYMB_END,    TONE_SYMBOL_END_PERIOD),
    101     ADD_SYMB_VALUE(TONE_SYMB_MUTE,   TONE_SYMBOL_MUTE_PERIOD),
    102     ADD_SYMB_VALUE(TONE_SYMB_LOW_1,  2865),
    103     ADD_SYMB_VALUE(TONE_SYMB_LOW_1H, 2703),
    104     ADD_SYMB_VALUE(TONE_SYMB_LOW_2,  2551),
    105     ADD_SYMB_VALUE(TONE_SYMB_LOW_2H, 2410),
    106     ADD_SYMB_VALUE(TONE_SYMB_LOW_3,  2273),
    107     ADD_SYMB_VALUE(TONE_SYMB_LOW_3H, 2155),
    108     ADD_SYMB_VALUE(TONE_SYMB_LOW_4,  2024),
    109     ADD_SYMB_VALUE(TONE_SYMB_LOW_4H, 1912),
    110     ADD_SYMB_VALUE(TONE_SYMB_LOW_5,  1805),
    111     ADD_SYMB_VALUE(TONE_SYMB_LOW_5H, 1704),
    112     ADD_SYMB_VALUE(TONE_SYMB_LOW_6,  1608),
    113     ADD_SYMB_VALUE(TONE_SYMB_LOW_6H, 1517),
    114     ADD_SYMB_VALUE(TONE_SYMB_LOW_7,  1000),
    115     ADD_SYMB_VALUE(TONE_SYMB_LOW_7H, 1000),
    116     ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_1,  1433),
    117     ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_1H, 1351),
    118     ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_2,  1276),
    119     ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_2H, 1205),
    120     ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_3,  1137),
    121     ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_3H, 1078),
    122     ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_4,  1012),
    123     ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_4H,  956),
    124     ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_5,   903),
    125     ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_5H,  852),
    126     ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_6,   804),
    127     ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_6H,  759),
    128     ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_7,  1000),
    129     ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_7H, 1000),
    130     ADD_SYMB_VALUE(TONE_SYMB_HIGH_1,  716),
    131     ADD_SYMB_VALUE(TONE_SYMB_HIGH_1H, 676),
    132     ADD_SYMB_VALUE(TONE_SYMB_HIGH_2,  638),
    133     ADD_SYMB_VALUE(TONE_SYMB_HIGH_2H, 603),
    134     ADD_SYMB_VALUE(TONE_SYMB_HIGH_3,  569),
    135     ADD_SYMB_VALUE(TONE_SYMB_HIGH_3H, 539),
    136     ADD_SYMB_VALUE(TONE_SYMB_HIGH_4,  506),
    137     ADD_SYMB_VALUE(TONE_SYMB_HIGH_4H, 478),
    138     ADD_SYMB_VALUE(TONE_SYMB_HIGH_5,  452),
    139     ADD_SYMB_VALUE(TONE_SYMB_HIGH_5H, 426),
    140     ADD_SYMB_VALUE(TONE_SYMB_HIGH_6,  402),
    141     ADD_SYMB_VALUE(TONE_SYMB_HIGH_6H, 379),
    142     ADD_SYMB_VALUE(TONE_SYMB_HIGH_7,  1000),
    143     ADD_SYMB_VALUE(TONE_SYMB_HIGH_7H, 1000),
    144     #undef ADD_SYMB_VALUE
    145 };
    146 
    147 // 每个节拍的周期
    148 static const uint16_t toneBeatDuration[TONE_BEAT_TOTAL] = {
    149     #ifdef ADD_BEAT_VAL_MS
    150     #undef ADD_BEAT_VAL_MS
    151     #endif
    152     #define ADD_BEAT_VAL_MS(E,V) (V)
    153     ADD_BEAT_VAL_MS(TONE_BEAT_END, 0),
    154     ADD_BEAT_VAL_MS(TONE_BEAT_4_1, 2000), // 4/1拍
    155     ADD_BEAT_VAL_MS(TONE_BEAT_3_1, 3000), // 3/1拍
    156     ADD_BEAT_VAL_MS(TONE_BEAT_2_1, 1000), // 2/1拍
    157     ADD_BEAT_VAL_MS(TONE_BEAT_3_2,  750), // 3/2拍
    158     ADD_BEAT_VAL_MS(TONE_BEAT_1_1,  500), // 1/1拍
    159     ADD_BEAT_VAL_MS(TONE_BEAT_3_4,  313), // 3/4拍
    160     ADD_BEAT_VAL_MS(TONE_BEAT_1_2,  250), // 1/2拍
    161     ADD_BEAT_VAL_MS(TONE_BEAT_1_3,  125), // 1/3拍
    162     ADD_BEAT_VAL_MS(TONE_BEAT_1_4,   63), // 1/4拍
    163     #undef ADD_BEAT_VAL_MS
    164 };
    165 
    166 struct tone_play_ctrl {
    167     uint32_t    arrCount;
    168     const struct tone_play_data *toneParam;
    169 };
    170 
    171 const struct tone_play_data beep_Du[] = {
    172     // 一闪一闪亮晶晶,1=C2/4
    173     {TONE_SYMB_HIGH_1, TONE_BEAT_1_3},
    174     // 最后一个元素必须是{0, 0}
    175     {TONE_SYMB_END, TONE_BEAT_END},
    176 };
    177 
    178 /*
    179 const struct tone_play_data testTone[] = {
    180     // 一闪一闪亮晶晶,1=C2/4
    181     {TONE_SYMB_MIDDLE_1, TONE_BEAT_1_2},
    182     {TONE_SYMB_MIDDLE_1, TONE_BEAT_1_2},
    183     {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2},
    184     {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2},
    185     {TONE_SYMB_MIDDLE_6, TONE_BEAT_1_2},
    186     {TONE_SYMB_MIDDLE_6, TONE_BEAT_1_2},
    187     {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_1},
    188 
    189     {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2},
    190     {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2},
    191     {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2},
    192     {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2},
    193     {TONE_SYMB_MIDDLE_2, TONE_BEAT_1_2},
    194     {TONE_SYMB_MIDDLE_2, TONE_BEAT_1_2},
    195     {TONE_SYMB_MIDDLE_1, TONE_BEAT_1_1},
    196 
    197     {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2},
    198     {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2},
    199     {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2},
    200     {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2},
    201     {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2},
    202     {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2},
    203     {TONE_SYMB_MIDDLE_2, TONE_BEAT_1_1},
    204 
    205     {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2},
    206     {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2},
    207     {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2},
    208     {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2},
    209     {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2},
    210     {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2},
    211     {TONE_SYMB_MIDDLE_2, TONE_BEAT_1_1},
    212 
    213     {TONE_SYMB_MIDDLE_1, TONE_BEAT_1_2},
    214     {TONE_SYMB_MIDDLE_1, TONE_BEAT_1_2},
    215     {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2},
    216     {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2},
    217     {TONE_SYMB_MIDDLE_6, TONE_BEAT_1_2},
    218     {TONE_SYMB_MIDDLE_6, TONE_BEAT_1_2},
    219     {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_1},
    220 
    221     {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2},
    222     {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2},
    223     {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2},
    224     {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2},
    225     {TONE_SYMB_MIDDLE_2, TONE_BEAT_1_2},
    226     {TONE_SYMB_MIDDLE_2, TONE_BEAT_1_2},
    227     {TONE_SYMB_MIDDLE_1, TONE_BEAT_1_1},
    228 
    229     // 最后一个元素必须是{0, 0}
    230     {TONE_SYMB_END, TONE_BEAT_END},
    231 };
    232 */
    233 
    234 // 播放控制变量,每次启动播放前必须初始化
    235 static struct tone_play_ctrl mTonePlayController;
    236 
    237 // 定时器中断回调,要是硬件自己处理该多好啊,这样的效率有点低
    238 // 定时器1&8有个RCR(repetition counter register),也许应该换定时器1&8的
    239 extern void TIM2_IRQHandler(void)
    240 {
    241     if (TIM_GetFlagStatus(TIM2, TIM_IT_Update))
    242     {
    243         // 进来一次说明经历了ARR时长
    244         mTonePlayController.arrCount ++;
    245         
    246         // duration单位是ms,ARR经过我们的配置后是1us,所以比较时要对duration乘以1000
    247         // 这里的音符的时长精度肯定有较大的误差,但是咱们暂不考虑处理这个
    248         if ((TIM2->ARR + 1) * mTonePlayController.arrCount >= toneBeatDuration[mTonePlayController.toneParam->beatIndex] * 1000)
    249         {
    250             mTonePlayController.toneParam++;
    251             if (TONE_BEAT_END < mTonePlayController.toneParam->beatIndex && TONE_SYMB_END < mTonePlayController.toneParam->symbolIndex)
    252             {
    253                 // 音符切换,必须重置arrCount,重设定时器的ARR寄存器和PWM的CCR寄存器
    254                 mTonePlayController.arrCount = 0;
    255                 TIM_SetAutoreload(TIM2, toneSymbolPeriod[mTonePlayController.toneParam->symbolIndex] - 1);
    256                 if (TONE_SYMB_MUTE == mTonePlayController.toneParam->symbolIndex)
    257                     TIM_SetCompare3(TIM2, toneSymbolPeriod[mTonePlayController.toneParam->symbolIndex]);
    258                 else
    259                     TIM_SetCompare3(TIM2, (toneSymbolPeriod[mTonePlayController.toneParam->symbolIndex] + 1) / 2);
    260             }
    261             else
    262             {
    263                 // 播放完毕了就关闭定时器
    264                 TIM_Cmd(TIM2, DISABLE);
    265                 TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable);
    266             }
    267         }
    268         
    269         TIM_ClearFlag(TIM2, TIM_IT_Update);
    270     }
    271 }
    272 
    273 /*
    274 STM32F411CC使用的外部晶振16M,所得到的sysclk为100MHZ
    275 */
    276 static void _tone_play_env_setup(void)
    277 {
    278     /* TIMER2, CHANNEL 3, PB10 */
    279     GPIO_InitTypeDef gpioInitData = {
    280         GPIO_Pin_10, 
    281         GPIO_Mode_AF,
    282         GPIO_Fast_Speed,
    283         GPIO_OType_PP,
    284         GPIO_PuPd_UP,
    285     };
    286     TIM_TimeBaseInitTypeDef timeData = {
    287         .TIM_CounterMode = TIM_CounterMode_Up,
    288         .TIM_ClockDivision = TIM_CKD_DIV1,
    289     };
    290     TIM_OCInitTypeDef ocdata = {
    291         .TIM_OCMode = TIM_OCMode_PWM2, // PWM模式2:CNT>CCR时输出有效
    292         .TIM_OutputState = TIM_OutputState_Enable,
    293         .TIM_OutputNState = TIM_OutputNState_Enable, // 仅Timer1&8, PWM模式用不上这个
    294         .TIM_OCPolarity = TIM_OCPolarity_High,
    295         .TIM_OCNPolarity = TIM_OCNPolarity_Low, // PWM模式用不上这个
    296         .TIM_OCIdleState = TIM_OCIdleState_Set, // PWM模式用不上这个
    297         .TIM_OCNIdleState = TIM_OCNIdleState_Reset, // PWM模式用不上这个
    298     };
    299 
    300     /* 定时器溢出周期,
    301        系统主频100MHz不分频(TIM_ClockDivision=TIM_CKD_DIV1)直接给到定时器2作为计数时钟,
    302        计数时钟信号100倍分频(TIM_Prescaler=100-1)就是1MHz,一个时钟周期就是1us。
    303        所以对计数时钟信号计数n个(TIM_Prescaler=n-1)就达到了n微秒。
    304        注意,我们后面主要控制 TIM_Prescaler 来达到音乐频率的控制。
    305        这里TIM_Prescaler=99是固定配置,一个计数时钟周期为1us,
    306        如果你使用的是72MHz(比如STM32F103),或者TIM_ClockDivision做了分频,要注意更改。
    307     */
    308     timeData.TIM_Prescaler = 100 - 1; 
    309     timeData.TIM_Period = 1000 - 1;
    310     // PWM占空比50%,就是 TIM_Period 的一半
    311     ocdata.TIM_Pulse = 500;
    312 
    313     // 我们要使用中断
    314     // NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    315     // RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    316     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    317 
    318     // 初始化GPIO以及功能复用
    319     GPIO_Init(GPIOB, &gpioInitData);
    320     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_TIM2);
    321 
    322     // 定时器中断分配
    323     NVIC_InitTypeDef nvicInitData;
    324     nvicInitData.NVIC_IRQChannel = TIM2_IRQn;
    325     nvicInitData.NVIC_IRQChannelPreemptionPriority = 2;
    326     nvicInitData.NVIC_IRQChannelSubPriority = 2;
    327     nvicInitData.NVIC_IRQChannelCmd = ENABLE;
    328     NVIC_Init(&nvicInitData);
    329     
    330     TIM_DeInit(TIM2);
    331     TIM_TimeBaseInit(TIM2, &timeData);
    332     TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 启用中断
    333     TIM_OC3Init(TIM2, &ocdata); //TIM2的通道2PWM 模式设置
    334     TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable); //使能预装载寄存器
    335     TIM_Cmd(TIM2, DISABLE); // 启动定时器
    336 }
    337 
    338 /*
    339 接口函数,注意形参传值的正确性
    340 */
    341 void tone_play(const struct tone_play_data *param)
    342 {
    343     if (NULL == param)
    344         return;
    345 
    346     xSemaphoreTake(semTonePlay, portMAX_DELAY);
    347     TIM_Cmd(TIM2, DISABLE);
    348     mTonePlayController.arrCount = 0;
    349     mTonePlayController.toneParam = param;
    350     TIM_SetAutoreload(TIM2, toneSymbolPeriod[mTonePlayController.toneParam->symbolIndex] - 1);
    351     TIM_SetCompare3(TIM2, (toneSymbolPeriod[mTonePlayController.toneParam->symbolIndex] + 1) / 2);
    352     TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
    353     TIM_Cmd(TIM2, ENABLE);
    354     xSemaphoreGive(semTonePlay);
    355 }
    356 
    357 void tone_stop(void)
    358 {
    359     xSemaphoreTake(semTonePlay, portMAX_DELAY);
    360     TIM_Cmd(TIM2, DISABLE);
    361     TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable);
    362     xSemaphoreGive(semTonePlay);
    363 }
    364 #endif
    View Code

    亲测可用。

    如果转载,请注明出处。https://www.cnblogs.com/ssdq/
  • 相关阅读:
    使用pwn_deploy_chroot部署国赛pwn比赛题目
    《Java程序设计》第十一章 JDBC与MySQL数据库
    使用commons.cli实现MyCP
    2018-2019-2 20175211 实验二《Java面向对象程序设计》实验报告
    结对编程练习_四则运算(第二周)
    20175211 2018-2019-2 《Java程序设计》第六周学习总结
    20175211 2017-2018-2 《Java程序设计》第六周学习记录(2)
    海思Hi35xx平台调试笔记
    ffmpeg,rtmpdump和nginx rtmp实现录屏,直播和录制
    文件传输(xmodem协议)
  • 原文地址:https://www.cnblogs.com/ssdq/p/13712853.html
Copyright © 2011-2022 走看看