【MM32】 eMiniBoard 之 PWM驱动无源蜂鸣器+ADC调整LED亮度 参考例程(上)

前言

本文介绍 MM32 Insight-Series 开发板 eMiniBoard 第二批出厂程序(基于 MM32-FDS 底层库开发)

主要功能

  1. 按键 * 4:控制对应 4 首内置歌曲的播放与暂停;
  2. LED * 4:显示当前的播放状态(处于播放状态时对应的 LED 慢闪);
  3. RV * 3:调节相应 LED * 3 (LED2LED3LED4)的亮度,调节过程显示其余两路 LED 状态。

准备工作

注:本文所有程序均基于 IAR EWARM 开发

1.官网下载 MM32-FDS 开发套件,支持 MM32 全系列版本,与Cortex-M微控制器软件接口标准(CMSIS)兼容。

2.官网下载相应 IAR_PACK 文件,支持 MM32 全系列芯片在 IAR v7.6 及以上版本的手动安装。

兼容版型

  • MM32-Insight 系列 MB020
  • MM32-Insight 系列 MB021
  • MM32-Insight 系列 MB022
  • MM32-Insight 系列 MB023
  • MM32-Insight 系列 MB024
  • MM32-Insight 系列 MB025

板级基础模块

硬件

LED & Key 模块

  • LED / Key 电路原理图如下所示
    MB020 电路原理图之 按键LED部分
  • LED 引脚表格
LED对应IO* 对应 Timer 通道
LD1PA_15*
LD2PB_3TIM2_CH2
LD3PB_4TIM3_CH1
LD4PB_5TIM3_CH2

* 本文使用的复用功能引脚,不代表该脚的全部复用功能,下同。

  • Key 引脚表格
Key对应IO
K1PB_1
K2PB_2
K3PB_10
K4PB_11

Beep 模块

Beep电路原理图

Beep对应IO对应 Timer 通道
BUZPA_8TIM1_CH1

RV模块

在这里插入图片描述

  • RV 引脚表格
RV对应IO对应AD通道
RV1PA_1ADC_Channel_1
RV2PA_4ADC_Channel_4
RV3PA_5ADC_Channel_5

软件

LED/Key 初始化代码


void BSP_LED_Configure(void);
void BSP_KEY_Configure(void);


void led2on()
{
    TIM_SelectOCxM  (TIM2, TIM_Channel_2, TIM_OCMode_PWM1);
    TIM_CCxCmd      (TIM2, TIM_Channel_2, TIM_CCx_Enable);
}
void led2off()
{
    TIM_SelectOCxM  (TIM2, TIM_Channel_2, TIM_OCMode_Inactive);
    TIM_CCxCmd      (TIM2, TIM_Channel_2, TIM_CCx_Enable);
}
void led3on()
{
    TIM_SelectOCxM  (TIM3, TIM_Channel_1, TIM_OCMode_PWM1);
    TIM_CCxCmd      (TIM3, TIM_Channel_1, TIM_CCx_Enable);
}
void led3off()
{
    TIM_SelectOCxM  (TIM3, TIM_Channel_1, TIM_OCMode_Inactive);
    TIM_CCxCmd      (TIM3, TIM_Channel_1, TIM_CCx_Enable);
}
void led4on()
{
    TIM_SelectOCxM  (TIM3, TIM_Channel_2, TIM_OCMode_PWM1);
    TIM_CCxCmd      (TIM3, TIM_Channel_2, TIM_CCx_Enable);
}
void led4off()
{
    TIM_SelectOCxM  (TIM3, TIM_Channel_2, TIM_OCMode_Inactive);
    TIM_CCxCmd      (TIM3, TIM_Channel_2, TIM_CCx_Enable);
}


void closeLED()
{
    LD6_off();
    led2off();
    led3off();
    led4off();
}


void initLEDPWM(u16 psc, u16 arr)
{
    GPIO_InitTypeDef        GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef       TIM_OCInitStructInit;
    
    COMMON_EnableIpClock(emCLOCK_GPIOB);
    COMMON_EnableIpClock(emCLOCK_TIM2);
    COMMON_EnableIpClock(emCLOCK_TIM3);
    
    GPIO_Mode_AF_PP_20MHz_Init(GPIOB, GPIO_Pin_3, EXTI_MAPR_TIM2_PARTIAL1, GPIO_AF_2);
    GPIO_Mode_AF_PP_20MHz_Init(GPIOB, GPIO_Pin_4, EXTI_MAPR_TIM3_PARTIAL, GPIO_AF_1);
    GPIO_Mode_AF_PP_20MHz_Init(GPIOB, GPIO_Pin_5, EXTI_MAPR_TIM3_PARTIAL, GPIO_AF_1);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP; 
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    TIM_TimeBaseStructure.TIM_Period = arr; 
    TIM_TimeBaseStructure.TIM_Prescaler = psc; 
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
    TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); 
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    
    TIM_OCStructInit(&TIM_OCInitStructInit);
    TIM_OCInitStructInit.TIM_OCMode = TIM_OCMode_PWM1; 
    TIM_OCInitStructInit.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructInit.TIM_Pulse = arr >> 1;
    TIM_OCInitStructInit.TIM_OCPolarity = TIM_OCPolarity_Low;
    TIM_OC2Init(TIM2, &TIM_OCInitStructInit);
    TIM_OC1Init(TIM3, &TIM_OCInitStructInit);
    TIM_OC2Init(TIM3, &TIM_OCInitStructInit);
    
    TIM_Cmd(TIM2, ENABLE);
    TIM_Cmd(TIM3, ENABLE);
}

重点说明:

  1. MM32-FDS默认配置完毕板载资源,只需定义宏 __MM32_MINIBOARD(MiniboardeMiniboard部分资源不兼容,后续开发将升级为对应开发板型号 宏定义 MBxxx),即可开启板载资源 LED/KEY
  2. LD6_off();Miniboard 原有配置函数,开启LED1
  3. void initLEDPWM(u16 psc, u16 arr) 为配置LED对应定时器PWM通道。实例化代码如下:
RCC_ClocksTypeDef rcc_clocks;

void main()
{
	RCC_GetClocksFreq(&rcc_clocks);
	initLEDPWM(rcc_clocks.SYSCLK_Frequency / 1000000 - 1, 4096);
	//...........
}

Beep 初始化代码


void initBuzzer(u16 prescaler)
{
    GPIO_InitTypeDef        GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef       TIM_OCInitStructInit;
    
    COMMON_EnableIpClock(emCLOCK_GPIOA);
    COMMON_EnableIpClock(emCLOCK_TIM1);
    
    GPIO_Mode_AF_PP_20MHz_Init(GPIOA, GPIO_Pin_8, NO_REMAP, GPIO_AF_2);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP; 
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    TIM_TimeBaseStructure.TIM_Period = 999; 
    TIM_TimeBaseStructure.TIM_Prescaler = prescaler; 
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
    TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); 
    
    TIM_OCStructInit(&TIM_OCInitStructInit);
    TIM_OCInitStructInit.TIM_OCMode = TIM_OCMode_PWM2; 
    TIM_OCInitStructInit.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructInit.TIM_Pulse = 0;
    TIM_OCInitStructInit.TIM_OCPolarity = TIM_OCPolarity_Low;
    TIM_OC1Init(TIM1, &TIM_OCInitStructInit);
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); 
    
    TIM_CtrlPWMOutputs(TIM1, ENABLE);
}

实例化代码:

RCC_ClocksTypeDef rcc_clocks;


void main()
{
	RCC_GetClocksFreq(&rcc_clocks);
	initBuzzer(rcc_clocks.SYSCLK_Frequency / 2000000 - 1);	
	//...........
}

RV 初始化代码


void initGPIO_ADC()
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    COMMON_EnableIpClock(emCLOCK_GPIOA);
    
    GPIO_InitStructure.GPIO_Pin  =  GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}


void initADC()
{
    ADC_InitTypeDef ADC_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;
    
    COMMON_EnableIpClock(emCLOCK_ADC1);
    COMMON_EnableIpClock(emCLOCK_DMA1);
    
    initGPIO_ADC();
    
    DMA_DeInit(DMA1_ch1);
    DMA_InitStructure.PeripheralBaseAddr = (u32)&(ADC1->DR);
    DMA_InitStructure.MemoryBaseAddr = (u32)&ADCtemp[0];
    DMA_InitStructure.DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.BufferSize = 3;
    DMA_InitStructure.PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.MemoryDataSize = DMA_MemoryDataSize_Word;
    DMA_InitStructure.Mode = DMA_Mode_Circular;
    DMA_InitStructure.Priority = DMA_Priority_High;
    DMA_InitStructure.M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_ch1, &DMA_InitStructure);
    DMA_Cmd(DMA1_ch1, ENABLE);
    
    ADC_InitStructure.ADC_Mode = ADC_CR_CONTINUE;
    ADC_InitStructure.ADC_PRESCARE = ADC_PCLK2_PRESCARE_16;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC1_ExternalTrigConv_T1_CC1;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_Init(ADC1, &ADC_InitStructure);
    
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1,  1, ADC_Samctl_1_5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4,  2, ADC_Samctl_1_5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5,  3, ADC_Samctl_1_5);
    
    ADC_ITConfig(ADC1, ADC_IT_EOC, DISABLE);
    ADC_Cmd(ADC1, ENABLE);
    ADC_DMACmd(ADC1, ENABLE);
    ADC1->CFGR |= 0x04;
    
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

蜂鸣器播放音频

单音频率计算

单音频率
本文根据上图的单音频率配置频谱表,如下所示:

音符选定频率定时器重载值
12627633
22946802
33306060
43495076
53925102
64404545
74944048
·15233824
·25873407
·3*3034
·4*2865
·5*2550
·6*2273
·7*2024
13115267
14713605
*12121
*11429
*10204
*9090
*8097
··110471910
··2*1702
··3*1516
··4*1432
··5*1276
··6*1136
··7*1012

说明:

  1. *为对应单音频谱省略数据
  2. 计算过程:
    系统主频:
  • MB022(HSI 48MHz)
  • MB023(HSE 96MHz)
  • MB024(HSE 96MHz)
  • MB025(HSE 96MHz)
    定时器分频后频率: 2MHz
    单音对应定时器重载值 = 2MHz / 单音频率
const u16 tonetime[] = {
    1000,
    // 1: do
    7633,   6802,   6060,   5076,   5102,   4545,   4048,1000,1000,1000,
    //11: `do
    3824,   3407,   3034,   2865,   2550,   2273,   2024,1000,1000,1000,
    //21: do`
    15267,  13605,  12121,  11429,  10204,  9090,   8097,1000,1000,1000,
    //31: ``do
    1910,   1702,   1516,   1432,   1276,   1136,   1012,1000,1000,1000,
};

说明:

  1. 数值 1000 为空白/占位,调度算法会将小于等于 1000 的数值屏蔽发音。

谱曲

以兰花草为例:
在这里插入图片描述
根据 简谱 编写music音调、time节拍 数组,如下所示:

u8 music[]={
    6,3,3,3,3,2,        1,2,1,27,26,        6,6,6,6,6,5,
    3,5,5,4,3,          3,6,6,5,3,2,        1,2,1,27,26,23,
    23,1,1,27,26,3,     2,1,27,25,26
};

u8 time[] = {
    2,2,2,2,6,2,        3,1,2,2,8,          2,2,2,2,6,2,
    2,2,2,2,8,          2,2,2,2,6,2,        2,2,2,2,4,4,
    2,2,2,2,6,2,        3,1,2,2,8 
};

说明:

  1. 《兰花草》第一拍的低音6(6`)调整为中音 6(6)

定时器设置

配置定时器重载值配置、定时器关闭功能。


void setBuzzerFreq(u16 Period)
{
    TIM_SetAutoreload(TIM1, Period);        
    TIM_SetCompare1(TIM1, Period / 2);      
}


void setBuzzerEn(FunctionalState NewState)
{
    TIM_Cmd(TIM1, NewState);
}

调度算法


void PlayMusic(void)
{
    u16 tonetemp;
    
    switch(sPlayMusic.PlayStep){
            
        case PLAYSTEP1:
        setBuzzerEn(DISABLE);
        if(true == sPlayMusic.PlayFlag){
            setBuzzerFreq(tonetime[music[sPlayMusic.ToneNumCount]]);
            sPlayMusic.PlayStep = PLAYSTEP2;         
        }
        break;
        
        case PLAYSTEP2:
        if(true == sPlayMusic.PlayFlag){
            // play music 0
            if(sPlayMusic.MusicNum == 0) {
                if(sPlayMusic.ToneNumCount < sizeof(music)/sizeof(u8)){
                    sPlayMusic.ToneCount++;
                    if(sPlayMusic.ToneCount <= (time[sPlayMusic.ToneNumCount] * 10 / 2)){
                        setBuzzerEn(ENABLE);
                    }
                    else {
                        setBuzzerEn(DISABLE);
                        if(sPlayMusic.ToneCount >= (time[sPlayMusic.ToneNumCount] * 10)){
                            sPlayMusic.ToneCount =0;
                            sPlayMusic.ToneNumCount ++;
                            tonetemp = tonetime[music[sPlayMusic.ToneNumCount]];
                            if(tonetemp <= 1000) 
                                setBuzzerEn(DISABLE);
                            else {
                                setBuzzerFreq(tonetemp);
                                setBuzzerEn(ENABLE);
                            }
                        }
                    }
                }
                else{
                    sPlayMusic.PlayStep = PLAYSTEP3;
                    setBuzzerEn(DISABLE);
                }
            }
            //.......................
            else{
                setBuzzerEn(DISABLE);
            }
        }
        break;
        
        case PLAYSTEP3:         
        sPlayMusic.PlayFlag = false;
        sPlayMusic.CurrentPlayFlag = false;
        sPlayMusic.MusicNum = 0;
        sPlayMusic.ToneCount = 0;
        sPlayMusic.ToneNumCount = 0;
        sPlayMusic.PlayStep = PLAYSTEP1;
        break; 
    }
}

系统调度


void AppTaskTick(void)
{
    if(ready){
        if (tickCnt++ >= 20) {
            tickCnt  = 0;
            tickFlag = true;
            PlayMusic();
        }
        //.....
    }
}

说明
1.本文配置的发音单元 发音短促,留有大约 50% 的空白,保持蜂鸣器短促悦耳。

篇幅有限,LED 状态闪烁及 RV 调整 LED 亮度参见 《MM32 eMiniBoard 之 PWM 驱动无源蜂鸣器+ ADC 调整 LED 亮度 参考例程(下)》

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值