基于stm32的智能门铃-课程设计
介绍
本人的处理器项目课程设计,设计简易,仅为记录学习所用。
代码还未优化,例如全局变量应使用消息队列替换、功能简单可无需使用操作系统等等。
课设题目要求

实现功能
- 按键控制蜂鸣器发声
- 语音控制
- 手机蓝牙控制
- 乐曲选择
硬件部分
主控
STM32F103C8T6
离线语音识别模块
ASR PRO
蓝牙模块
大夏龙雀 BT24 低功耗蓝牙模块
PCB原理图

软件部分
ASR PRO
中文编程,实现简单的离线语音识别。
与STM32串口通信,收发数据简单,无需使用数据包

手机端蓝牙
蓝牙调试助手app

STM32
CubeMX+CLion/CubeIDE/Keil
CubeMX配置
- 
时钟 
  
  
- 
debug与滴答时钟 
  
- 
I2C(OLED) 
  
- 
ASR PRO串口与蓝牙串口 
 ASR PRO串口
  
  
 蓝牙串口
  
  
  
- 
按键与led灯 
  
- 
PWM 
 呼吸灯
  
 蜂鸣器
  
- 
FreeRTOS 
 基本设置
  
 任务与消息队列
  
主要代码
OLED库:波特律动驱动库
DMA实现:FreeRTOS 消息队列 DMA串口空闲中断接收消息
全局变量
int request=0,overTM=0;//按下请求、播放次数
char countTM[32]={0};
int musicc=0;//曲谱切换标志位
const uint16_t notes1[] = {0, 261, 294, 329, 349, 392, 440, 493};
const uint8_t melody1[] = {1, 1, 5, 5, 6, 6, 5, 0, 4, 4, 3, 3, 2, 2, 1, 0};
const uint16_t notes2[] = {0,523,587,659,698,784,880,988,1046,1175,1318,1397,1568,1760,1976};
const uint8_t melody2[] = {0,0,9,9,6,7,6,5,3,5,2,2,2,2,0,0,9,10,6,7,6,5,3,5,6,6,6,6,9,9,6,7,6,6,6,5,6,6,6,5,3,3,3,3,3,3,3,6,6,6,5,3,2,2,2,2,0,0,2,2,3,3,5,5,3,3,6,6,6,6,0,0,7,7,7,6,5,5,6,5,3,3,3,3,3,3,3,3,3,3,3,5,5,6,6,6,7,6,6,5,5,3,3,3,5,6,5,5,3,2,2,2,2,2};
static uint8_t send_data[] = {0xA5, 0x00, 0x00, 0x5A};
uint8_t rx_data[2];// 串口接收缓冲区
uint8_t blu=0,spe=0;
LED呼吸灯任务
void vmainTask(void const * argument)
{
  /* USER CODE BEGIN vmainTask */
	// 开启串口中断接收
	HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
	HAL_UART_Receive_IT(&huart2, rx_data, 2);
  /* Infinite loop */
  for(;;)
  {
	  if(request==1){
		  for (int period = 0; period < 100; period++){
			  __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, period);
			  osDelay(10);
			  if(request==0)
				  __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 0);
		  }
	  }
	  if(request==1){
		  for (int period = 99; period >= 0; period--){
			  __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, period);
			  osDelay(10);
			  if(request==0)
				  __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 0);
		  }
	  }
	  if(request==0)
		  __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 0);
    osDelay(1);
  }
  /* USER CODE END vmainTask */
}
音频播放任务与功能函数
void vPlayTask(void const * argument)
{
  /* USER CODE BEGIN vPlayTask */
  /* Infinite loop */
  for(;;)
  {
	if(request==1&&musicc==0)
	{
		for(overTM=0;overTM<8;overTM++)
		{
			for (int j = 0; j < sizeof(melody1)/sizeof(melody1[0]); j++)
			{
				PlayNote(notes1[melody1[j]]);
				if(request==0){
					  HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_4);
					  break;
				}
			    osDelay(500); // 每拍0.5秒
			}
			if(request==0)
				break;
		}
		request=0;
	}
	if(request==1&&musicc==1)
	{
		for(overTM=0;overTM<8;overTM++)
		{
			for (int j = 0; j < sizeof(melody2)/sizeof(melody2[0]); j++)
			{
				PlayNote(notes2[melody2[j]]);
				if(request==0){
					  HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_4);
					  break;
				}
			    osDelay(250); // 每拍0.25秒
			}
			if(request==0)
				break;
		}
		request=0;
	}
    osDelay(1);
  }
  /* USER CODE END vPlayTask */
}
void PlayNote(uint16_t frequency)
{
	HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_4);//打开蜂鸣器pwm输出
	if(frequency>0)
	{
		htim4.Instance->ARR = 1000000/frequency;
		__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_4, htim4.Instance->ARR / 5);
	}
    else
    {
    	__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_4, 0);
    }
}
OLED显示任务
void vOLEDTask(void const * argument)
{
  /* USER CODE BEGIN vOLEDTask */
	  osDelay(20);
	  OLED_Init(); // 初始化OLED
	  OLED_NewFrame();
  /* Infinite loop */
  for(;;)
  {
	OLED_NewFrame();
	OLED_PrintString(0, 0, "播放次数:", &font16x16, OLED_COLOR_NORMAL);
	OLED_PrintString(0, 17, "语音识别:", &font16x16, OLED_COLOR_NORMAL);
	OLED_PrintString(0, 33, "蓝牙:", &font16x16, OLED_COLOR_NORMAL);
	OLED_PrintString(0, 49, "乐曲:", &font16x16, OLED_COLOR_NORMAL);
	sprintf(countTM, "%d", overTM);
	OLED_PrintString(81, 0,countTM, &font16x16, OLED_COLOR_NORMAL);
	if(blu==1)
		OLED_PrintString(81, 33, "已连接", &font16x16, OLED_COLOR_NORMAL);
	else
		OLED_PrintString(81, 33, "未连接", &font16x16, OLED_COLOR_NORMAL);
	if(spe==1)
		OLED_PrintString(81, 17, "已连接", &font16x16, OLED_COLOR_NORMAL);
	else
		OLED_PrintString(81, 17, "未连接", &font16x16, OLED_COLOR_NORMAL);
	if(musicc==0)
		OLED_PrintString(81, 49, "1", &font16x16, OLED_COLOR_NORMAL);
	else
		OLED_PrintString(81, 49, "2", &font16x16, OLED_COLOR_NORMAL);
	OLED_ShowFrame();
    osDelay(10);
  }
  /* USER CODE END vOLEDTask */
}
蓝牙串口接收任务(参考DMA实现)
void vBULETask(void const * argument)
{
  /* USER CODE BEGIN vBULETask */
    UART_RX_TypeDef *pRecvUartData; /* 定义指向串口数据的指针 */
    uint8_t *data_ptr;
  /* Infinite loop */
  for(;;)
  {
      if (xQueueReceive(blueQueueHandle, &pRecvUartData, portMAX_DELAY) == pdTRUE)
      {
          data_ptr = pRecvUartData->buffer;
          HAL_UART_Transmit(&huart3, pRecvUartData->buffer, pRecvUartData->size, 1000);
          if (data_ptr[1] == 0x01&&data_ptr[2]==0x01){
        	  HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_4);
        	  request=0;
        	  overTM=0;
          }
          if(data_ptr[2]==0x01){
        	  blu=1;
          }
          else if(data_ptr[2]==0x00){
        	  blu=0;
          }
      }
    osDelay(1);
  }
  /* USER CODE END vBULETask */
}
蓝牙串口发送与运行状态灯任务
void vTXBLUETask(void const * argument)
{
  /* USER CODE BEGIN vTXBLUETask */
	uint8_t aicyx=0;
  /* Infinite loop */
  for(;;)
  {
	switch(overTM){
		case 0:break;
		case 1:send_data[1]=send_data[2]=0x01;break;
		case 2:send_data[1]=send_data[2]=0x02;break;
		case 3:send_data[1]=send_data[2]=0x03;break;
		case 4:send_data[1]=send_data[2]=0x04;break;
		case 5:send_data[1]=send_data[2]=0x05;break;
		case 6:send_data[1]=send_data[2]=0x06;break;
		case 7:send_data[1]=send_data[2]=0x07;break;
		case 8:send_data[1]=send_data[2]=0x08;break;
	}
	if(blu==1)
		HAL_UART_Transmit(&huart3, send_data, sizeof(send_data), 1000);
	aicyx=~aicyx;
	HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, aicyx);
    osDelay(500);
  }
  /* USER CODE END vTXBLUETask */
}
按键中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  // 确认一下是否为KEY1按下
  if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0){
	  HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_4);
	  request=0;
	  overTM=0;
      // 等待KEY1松开
      while(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0);
  }
  // 确认一下是否为KEY2按下
  if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 0){
	  request=1;
	  overTM=0;
      // 等待KEY2松开
      while(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 0);
  }
  if(HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin) == 0){
  	  if(musicc==0)
  		  musicc=1;
  	  else
  		  musicc=0;
        // 等待KEY3松开
        while(HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin) == 0);
    }
}
语音串口中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
	if (huart->Instance == USART2){
		if(rx_data[0]=='1'&&rx_data[1]=='0'){
			  request=1;
			  overTM=0;
		}
		else if(rx_data[0]=='0'&&rx_data[1]=='1'){
			  HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_4);
			  request=0;
			  overTM=0;
		}
		else if(rx_data[0]=='0'&&rx_data[1]=='0'){
			spe=1;
		}
		HAL_UART_Receive_IT(&huart2, rx_data, 2);
	}
}
音频1为题目要求音乐
音频2为马云之歌(简单整活,音频实现过于简易)
注意:任务间尽量不要使用全局变量传递消息(虽然此程序使用全局变量并未发现bug)。博主当时还未深刻学习FreeRTOS,不知全局变量风险。
源码地址:https://gitee.com/ZhouYuxin123/smart-doorbell---course-design/tree/master

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号