51单片机用矩阵按键控制无源蜂鸣器发Do Re Mi Fa So La Si Do,用定时器实现
时间: 2024-01-24 09:19:13 浏览: 137
好的,这个可以通过矩阵按键和定时器来实现。以下是基本思路:
1. 连接矩阵按键和无源蜂鸣器,其中矩阵按键需要使用外部中断来检测按键是否被按下。
2. 配置定时器,使其以一定的频率产生中断。比如我们设定定时器每100ms触发一次中断。
3. 在中断服务函数中,检测当前按下的矩阵按键,并根据对应的按键按下时间来控制无源蜂鸣器的发声。
4. 根据Do Re Mi Fa So La Si Do对应的频率,来设置控制无源蜂鸣器的定时器周期,从而达到发出对应的音调。
下面是具体的实现步骤:
1. 连接矩阵按键和无源蜂鸣器
矩阵按键可以采用4×4矩阵按键或者其他类型的矩阵按键。无源蜂鸣器需要连接到单片机的一个IO口上。
2. 配置定时器
在配置定时器之前,需要先计算出对应音调的频率,然后根据频率计算出定时器的周期。这里以Do为例,其频率为262Hz,定时器的周期可以通过以下公式计算:
$$ T = \dfrac{1}{2 \times f} = \dfrac{1}{2 \times 262} = 1.91ms $$
其中,T表示定时器的周期,f为频率。
在具体的代码实现中,我们可以使用定时器2和定时器3来控制无源蜂鸣器的发声。定时器2用于产生中断,计算按键按下时间;定时器3用于控制无源蜂鸣器的发声。
以下是定时器2的初始化代码:
```c
void TIM2_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
/* Enable TIM2 clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* TIM2 configuration */
TIM_TimeBaseInitStruct.TIM_Prescaler = SystemCoreClock / 1000000 - 1; // 1us per tick
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1; // 10ms overflow
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
/* TIM2 interrupt configuration */
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
/* Enable TIM2 interrupt */
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
/* Start TIM2 */
TIM_Cmd(TIM2, ENABLE);
}
```
这里我们将定时器2配置为每1us产生一个tick,计数器溢出时间为10ms。同时,我们启用了TIM2的中断,并将TIM2_IRQn配置为定时器2的中断向量。
3. 中断服务函数实现
在定时器2的中断服务函数中,我们需要检测当前按下的矩阵按键,并根据对应的按键按下时间来控制无源蜂鸣器的发声。
首先,我们需要定义一个全局变量来表示当前按下的矩阵按键:
```c
uint8_t g_keycode = 0;
```
然后,在定时器2的中断服务函数中,我们首先检测当前按下的矩阵按键。如果没有按键按下,则直接返回。如果有按键按下,则记录按键按下时间,并开启定时器3来控制无源蜂鸣器的发声。
以下是定时器2的中断服务函数实现:
```c
void TIM2_IRQHandler(void)
{
static uint16_t s_tick = 0;
static uint8_t s_keydown = 0;
/* Clear TIM2 interrupt flag */
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
/* Detect matrix key */
uint8_t keycode = MatrixKeyRead();
if (keycode != 0 && s_keydown == 0)
{
s_keydown = 1;
g_keycode = keycode;
s_tick = 0;
/* Enable TIM3 to start beep */
TIM_Cmd(TIM3, ENABLE);
}
else if (keycode == 0 && s_keydown == 1)
{
s_keydown = 0;
g_keycode = 0;
/* Disable TIM3 to stop beep */
TIM_Cmd(TIM3, DISABLE);
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
}
else if (s_keydown == 1)
{
s_tick++;
}
}
```
上述代码中,我们使用了一个静态变量s_tick来记录按键按下的时间。同时,我们还使用了一个静态变量s_keydown来表示当前是否有按键按下。如果有按键按下,则s_keydown为1,否则为0。
在检测到按键按下的时候,我们记录下当前按键的编码,并将s_tick清零。然后,我们开启定时器3来控制无源蜂鸣器的发声。如果检测到按键松开,则将g_keycode清零,并关闭定时器3来停止发声。
4. 控制无源蜂鸣器的发声
在定时器3的中断服务函数中,我们需要根据Do Re Mi Fa So La Si Do对应的频率,来设置控制无源蜂鸣器的定时器周期,从而达到发出对应的音调。
以下是定时器3的初始化代码:
```c
void TIM3_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;
/* Enable TIM3 clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
/* TIM3 configuration */
TIM_TimeBaseInitStruct.TIM_Prescaler = SystemCoreClock / 1000000 - 1; // 1us per tick
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 382 - 1; // Do frequency 262Hz
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
/* TIM3 PWM configuration */
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 191;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStruct);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
/* Start TIM3 */
TIM_Cmd(TIM3, DISABLE);
}
```
这里我们将定时器3配置为每1us产生一个tick,计数器溢出时间为382us,对应Do音调的频率为262Hz。同时,我们使用了PWM输出来控制无源蜂鸣器的发声。
在定时器3的中断服务函数中,我们需要根据当前按下的矩阵按键,来设置定时器3的周期和PWM占空比,从而控制无源蜂鸣器的发声。
以下是定时器3的中断服务函数实现:
```c
void TIM3_IRQHandler(void)
{
/* Clear TIM3 interrupt flag */
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
/* Set TIM3 period and pulse according to current keycode */
switch (g_keycode)
{
case 1: // Do
TIM_SetAutoreload(TIM3, 382);
TIM_SetCompare1(TIM3, 191);
break;
case 2: // Re
TIM_SetAutoreload(TIM3, 340);
TIM_SetCompare1(TIM3, 170);
break;
case 3: // Mi
TIM_SetAutoreload(TIM3, 303);
TIM_SetCompare1(TIM3, 152);
break;
case 4: // Fa
TIM_SetAutoreload(TIM3, 286);
TIM_SetCompare1(TIM3, 143);
break;
case 5: // So
TIM_SetAutoreload(TIM3, 255);
TIM_SetCompare1(TIM3, 128);
break;
case 6: // La
TIM_SetAutoreload(TIM3, 227);
TIM_SetCompare1(TIM3, 113);
break;
case 7: // Si
TIM_SetAutoreload(TIM3, 202);
TIM_SetCompare1(TIM3, 101);
break;
default: // Silence
TIM_SetAutoreload(TIM3, 0xFFFF);
TIM_SetCompare1(TIM3, 0);
break;
}
}
```
上述代码中,我们根据当前按下的矩阵按键来设置定时器3的周期和PWM占空比。如果没有按键按下,则将定时器3的周期设置为0xFFFF,表示停止发声。
最后,我们需要在主函数中调用TIM2_Init()和TIM3_Init()函数来初始化定时器。同时,我们需要开启外部中断来检测矩阵按键的按下。
完整代码如下:
阅读全文