CUBEMX HAL库 FREERTOS STM32H723 练习项目:简易MP3播放器(U8G2多级菜单)

        很久没有更新了,最近情绪有点低落(感情受挫呜呜呜),遂来此跟新一下博客,还是经典存货HH

该项目完结于半年以前,所以有些细节现在记不清了,其中关键的最佳按键消抖技术会在下一篇博客里专门讲解。

本文完全原创,初始版本为课程仿真报告,转载与作业使用请务必注明出处!

目  录

第一章 整体方案及性能指标

    1. 整体方案

本工程采用 PROTEUS 仿真和 CUBEMX 生成初始代码, KEIL DEBUG+VSCODE 编译 进行开发使用HAL 库(由于已经掌握标准库用HAL 库练手、易于移植)和FREERTOS 实现 OLED PROTEUS 内部编号 OLED12863I2C)的多级菜单(采用 U8G2 图形库),功能包含通过外部中断进行操作, RTOS 保证实时性,实现多任务系统,蜂鸣器在音乐菜单进行简单音乐播放(共两首用于展示,可自由添加),同时实现流水灯(共三个动画用于展示,其余自由添加)操作与变换。

实物硬件平台为 STM32H723ZGT6

仿真硬件平台为 STM32F103C8

    1. 问题描述

硬件问题:电路板上的元件可能会损坏或者失效,导致设备无法正常工作。此外,电路板的设计可能存在问题,例如电路板上的元件之间的连接可能不正确,或者电路板上的元件可能不兼容。

软件问题:软件可能存在错误或者缺陷,导致设备无法正常工作,最常见的是任务调度的实时性,按键检测的实时性,任务间通信的解耦

供电问题:设备可能无法正常工作,因为它没有足够的电源。例如,如果使用的是电池供电,电池可能会耗尽电量,导致设备无法正常工作。

    1. 方案设计

见流程图

    1. 性能指标

播放流畅度(人体难以感知变换),按键消抖(99%消抖,日常实验没有一次多跳转),响应时间<10ms,CPU 占用率(使用 FREERTOS 的反馈函数打印),任务调度,信息传输,屏幕刷新速度。CPU 占用率(无论是音乐的重装还是 LED 变换对CPU 占用率都极低,所以主要还是更新屏幕更耗时间)堆栈使用优先级等调用串口与内部函数生成实时调用与堆栈占用

Task name  Run Count    usage

PrintCPU    41658      13%

defaultTask    58020   18%  

Tmr Svc  0   <1  %

IDLE  0   <1%

LEDPLAYTask  0   <1%

MUSICPLAYTask 0 <1%

OLEDSendBuffer   205789 67%

InitTask 74  <1%

EXITCHECK 0   <1%

Task Task status Priority Number of the remaining

stack task PrintCPU X   40  187 7

OLEDSendBuffer   R   9   433 4

defaultTask  R     8   245 1

Tmr Svc    R 2   246    9

IDLE        R    0   118 8

MUSICPLAYTask    B   993 3

LEDPLAYTask B    9   483 2

InitTask D   40    198 6

EXITCHECK S   11    231 5

其中音乐播放因为要存储歌曲,占用堆栈较大,LED要存储播放效果,堆栈大一些。初始化任务只在一开始运行,运行结束之后就会关闭。

第二章 仿真原理

    1. STM32简介

STM32单片机主要是由意法半导体公司设计的微控制器,其具有低功耗、低成本和高性能的特点,适用于嵌入式应用。其采用ARM Cortex-O内核,根据其内核架构的不同,可以将其分成一系列产品,当前主流的产品包括STM32F0、STM32F1、STM32F3,具有超低功耗的产品包括STM32L0、STM32L1、STM32L4等。由于STM32单片机中应用的内核具有先进的架构,使其在实施性能以及功耗控制等方面都具有较强表现,因此在整合和集成方面就有较大的优势,开发起来较为方便,该类型的单片机能非常迅速地实现开发和投入市场,当前市场中这种类型的单片机十分常见,类型多样,包括基础型、智能型和高级型等,应用都比较广泛。

说明:总线与外设均以 H723 为标准

    1. 硬件使用类与软件使用类

硬件使用类:

  1. GPIO

GPIO(AHB4)输出(在F1 上使用模拟I2C-开漏输出—PROTUES不支持硬件I2C)(点亮 LED)GPIO 输入(外部中断)挂在于 APB1 总线

GPIO配置

void MX_GPIO_Init(void)

{



  GPIO_InitTypeDef GPIO_InitStruct = {0};



  /* GPIO Ports Clock Enable */

  __HAL_RCC_GPIOH_CLK_ENABLE();

  __HAL_RCC_GPIOA_CLK_ENABLE();

  __HAL_RCC_GPIOB_CLK_ENABLE();



  /*Configure GPIO pin Output Level */

  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);



  /*Configure GPIO pin Output Level */

  HAL_GPIO_WritePin(GPIOA, LED2_Pin|LED3_Pin, GPIO_PIN_SET);



  /*Configure GPIO pins : PAPin PAPin PAPin PAPin */

  GPIO_InitStruct.Pin = KEY1_Pin|KEY2_Pin|KEY3_Pin|KEY4_Pin;

  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;

  GPIO_InitStruct.Pull = GPIO_PULLUP;

  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);



  /*Configure GPIO pin : PtPin */

  GPIO_InitStruct.Pin = LED1_Pin;

  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

  GPIO_InitStruct.Pull = GPIO_NOPULL;

  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

  HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);



  /*Configure GPIO pins : PAPin PAPin */

  GPIO_InitStruct.Pin = LED2_Pin|LED3_Pin;

  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

  GPIO_InitStruct.Pull = GPIO_PULLUP;

  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);



  /* EXTI interrupt init*/

  HAL_NVIC_SetPriority(EXTI1_IRQn, 5, 0);

  HAL_NVIC_EnableIRQ(EXTI1_IRQn);



  HAL_NVIC_SetPriority(EXTI2_IRQn, 5, 0);

  HAL_NVIC_EnableIRQ(EXTI2_IRQn);



  HAL_NVIC_SetPriority(EXTI3_IRQn, 5, 0);

  HAL_NVIC_EnableIRQ(EXTI3_IRQn);



  HAL_NVIC_SetPriority(EXTI4_IRQn, 5, 0);

  HAL_NVIC_EnableIRQ(EXTI4_IRQn);



}

LED动画

#include "stm32h7xx.h"

#include "led.h"

#include "main.h"

#include "freertos.h"

#include "menu.h"

void led_cartoon1(void)

{

    for(;;)

    {

        LED1(1);

        LED2(1);

        LED3(1);

        osDelay(30);

        if(led_play_use==3 || led_play_use==2)

        {

            LEDOFF;

            break;

        }

        LED1(0);

        LED2(1);

        LED3(1);

        osDelay(80);

        if(led_play_use==3 || led_play_use==2)

        {

            LEDOFF;

            break;

        }

        LED1(1);

        LED2(0);

        LED3(1);

        osDelay(10);

        LED1(1);

        LED2(1);

        LED3(0);

        osDelay(100);

        if(led_play_use==3 || led_play_use==2)

        {

            LEDOFF;

            break;

        }

    }

    xSemaphoreGive(xSemaphore);//释放互斥量

    if(led_playflag!=1)

    {

        LEDOFF;

    }

}



void led_cartoon2(void)

{

    for(;;)

    {

        LED1(1);

        LED2(0);

        LED3(0);

        osDelay(50);

        if(led_play_use==3 || led_play_use==1)

        {

            LEDOFF;

            break;

        }

        LED1(0);

        LED2(1);

        LED3(0);

        osDelay(50);

        if(led_play_use==3 || led_play_use==1)

        {

            LEDOFF;

            break;

        }

        LED1(0);

        LED2(0);

        LED3(1);

        osDelay(10);

        LED1(1);

        LED2(1);

        LED3(1);

        osDelay(30);

    }

    xSemaphoreGive(xSemaphore);//释放互斥量

    if(led_playflag!=2)

    {

        LEDOFF;

    }

}



void led_cartoon3(void)

{

    for(;;)

    {

        LED1(1);

        LED2(1);

        LED3(1);

        osDelay(10);

        LED1(0);

        LED2(0);

        LED3(0);

        osDelay(10);

        LED1(1);

        LED2(0);

        LED3(1);

        if(led_play_use==2 || led_play_use==1)

        {

            LEDOFF;

            break;

        }

        osDelay(50);

        LED1(0);

        LED2(1);

        LED3(0);        

        if(led_play_use==2 || led_play_use==1)

        {

            LEDOFF;

            break;

        }

        osDelay(35);



    }

    xSemaphoreGive(xSemaphore);//释放互斥量

    if(led_playflag!=3)

    {

        LEDOFF;

    }

}
  1. TIM3(APB1)时钟进行 PWM 输出,使蜂鸣器播放音乐

#   include "buzzed.h"

#include "stm32h7xx.h"

#include "tim.h"

#include "menu.h"

extern volatile char music_flag,music_playflag,music_playflag_pre;

extern volatile char led_flag,led_playflag;

const int wind_rise[]=

{  

    M2,50,M2,50,M1,25,M2,50,M2,50,M1,25,M2,50,M3,50,M5,50,M3,50, M2,50,M2,50,M1,25,M2,50,M2,50,M1,25,M2,25,M3,25,M2,25,M1,25,L6,100,Z0,10,

    //迈出车站的前一刻 竟有些犹豫

    M2,50,M2,50,M1,25,M2,50,M2,50,M1,25,M2,50,M3,50,M5,50,M3,50, M2,50,M2,50,M3,25,M2,50,M1,50,M2,100,Z0,50,

    //不仅笑着这近乡情怯 仍无法避免

    M2,50,M2,50,M1,25,M2,50,M2,50,M1,25,M2,50,M3,50,M5,50,M3,50, M2,50,M2,50,M3,25,M2,50,M1,50,L6,100,Z0,10,

};

void Wind_Rises(void)

{

    int length = sizeof(wind_rise)/sizeof(wind_rise[0]);

        HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);

        for(int i=0;i<(length/2) && music_playflag==1;i++)

        {

            if(playflag==2)

            {

                buzzer_off();

                break;

            }

            buzzer_on(wind_rise[i*2],260);

}

const int solitary_brave[]=

{

    M6,50,M7,50,H1,50,H2,50,M7,50,H1,50,H1,100,Z0,10,   //爱你孤身走暗巷

    H1,50,M7,50,H1,50,H2,50,M7,50,H1,50,H1,100,Z0,10,   //爱你不跪的模样

    H1,50,H2,50,H3,50,H2,50,H3,50,H2,50,H3,100,H3,50,H3,50,H2,50,H3,100,H5,100,H3,100,Z0,10 //爱你对峙过绝望不肯哭一场

};

void Solitary_brave(void)

{

        HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);

        int length = sizeof(solitary_brave)/sizeof(solitary_brave[0]);

        for(int i=0;i<(length/2) && music_playflag==2;i++)

        {

            if(playflag==1)

            {

                buzzer_off();

                break;

            }

            buzzer_on(solitary_brave[i*2],500);

            osDelay(5*solitary_brave[i*2+1]);

        }

        if(music_playflag!=2)

        {

            buzzer_off();

        }

}

void buzzer_on(uint16_t psc, uint16_t pwm)

{

    __HAL_TIM_PRESCALER(&htim3, psc);

    __HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_1, pwm);

     

}

void buzzer_off(void)

{

      HAL_TIM_PWM_Stop_IT(&htim3, TIM_CHANNEL_1);

    __HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_1, 0);

      HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15,GPIO_PIN_RESET);

      osDelay(1);

}

TIM4(APB1)+USART1(APB2)进行 CPU 利用率和任务调度

串口配置

#include "usart.h"



/* USER CODE BEGIN 0 */

int fputc(int ch, FILE *f)

{

  uint8_t temp[1] = {ch};

  HAL_UART_Transmit(&huart1, temp, 4, 10);//huart1需要根据你的配置修改

  return ch;

}

int fgetc(FILE *f)

{

  uint8_t ch = 0;

  HAL_UART_Receive(&huart1, &ch, 1, 0xffff);

  return ch;

}



/* USER CODE END 0 */



UART_HandleTypeDef huart1;



/* USART1 init function */



void MX_USART1_UART_Init(void)

{

  huart1.Instance = USART1;

  huart1.Init.BaudRate = 115200;

  huart1.Init.WordLength = UART_WORDLENGTH_8B;

  huart1.Init.StopBits = UART_STOPBITS_1;

  huart1.Init.Parity = UART_PARITY_NONE;

  huart1.Init.Mode = UART_MODE_TX_RX;

  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;

  huart1.Init.OverSampling = UART_OVERSAMPLING_16;

  huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;

  huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;

  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;

  if (HAL_UART_Init(&huart1) != HAL_OK)

  {

    Error_Handler();

  }

  if (HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)

  {

    Error_Handler();

  }

  if (HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)

  {

    Error_Handler();

  }

  if (HAL_UARTEx_DisableFifoMode(&huart1) != HAL_OK)

  {

    Error_Handler();

  }

}



void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)

{



  GPIO_InitTypeDef GPIO_InitStruct = {0};

  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  if(uartHandle->Instance==USART1)

  {

    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART1;

    PeriphClkInitStruct.Usart16ClockSelection = RCC_USART16910CLKSOURCE_D2PCLK2;

    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)

    {

      Error_Handler();

    }

    __HAL_RCC_USART1_CLK_ENABLE();



    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;

    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

    GPIO_InitStruct.Pull = GPIO_NOPULL;

    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;

    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);

    HAL_NVIC_EnableIRQ(USART1_IRQn);

  }

}

进行统计并发送至上位机

SYSTICK 作为 FREERTOS 时钟基

HAL 时钟基为 TIM1(挂在于 APB2 总线)

硬件I2C1(FAST MODE PLUS)(APB1)

软件使用类:

HAL 库和CUBEMX 进行可视化配置(含配置 FREERTOS)

引脚配置和中断配置

    1. FREERTOS 任务调度

核心代码(去除部分代码减小篇幅)如下:

主函数

int main(void)

{

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();

  MX_I2C1_Init();

  MX_TIM3_Init();

  MX_USART1_UART_Init();

  MX_TIM4_Init();

  osKernelInitialize();  /* Call init function for freertos objects (in freertos.c) */

  MX_FREERTOS_Init();

  /* Start scheduler */

  osKernelStart();

  /* 任务调度开启,不会执行到后面We should never get here as control is now taken by the scheduler */

  /* Infinite loop */

  /* USER CODE BEGIN WHILE */

  while (1)

  {

  }

}

FREERTOS任务文件:

#include "oled_app.h"

#include "driver_oled.h"

#include "stm32h7xx.h"

#include "u8g2.h"

#include "MENU.H"

#include "gpio.h"

#include "led.h"

#include "usart.h"

#include "FreeRTOS.h"

#include "task.h"

#include "main.h"

#include "cmsis_os.h"

#include "queue.h"

#include "semphr.h"

#include "tim.h"

SemaphoreHandle_t xSemaphore = NULL;

osThreadId_t defaultTaskHandle;

const osThreadAttr_t defaultTask_attributes = {

  .name = "defaultTask",

  .stack_size = 256 * 4,

  .priority = (osPriority_t) osPriorityLow,

};

/* Definitions for LED_PLAY_Task */

osThreadId_t LED_PLAY_TaskHandle;

const osThreadAttr_t LED_PLAY_Task_attributes = {

  .name = "LED_PLAY_Task",

  .stack_size = 512 * 4,

  .priority = (osPriority_t) osPriorityLow1,

};

/* Definitions for MUSIC_PLAY_Task */

osThreadId_t MUSIC_PLAY_TaskHandle;

const osThreadAttr_t MUSIC_PLAY_Task_attributes = {

  .name = "MUSIC_PLAY_Task",

  .stack_size = 1024 * 4,

  .priority = (osPriority_t) osPriorityLow1,

};

/* Definitions for OLED_SendBuffer */

osThreadId_t OLED_SendBufferHandle;

const osThreadAttr_t OLED_SendBuffer_attributes = {

  .name = "OLED_SendBuffer",

  .stack_size = 512 * 4,

  .priority = (osPriority_t) osPriorityLow1,

};

/* Definitions for EXIT_CHECK */

osThreadId_t EXIT_CHECKHandle;

const osThreadAttr_t EXIT_CHECK_attributes = {

  .name = "EXIT_CHECK",

  .stack_size = 256 * 4,

  .priority = (osPriority_t) osPriorityLow3,

};

/* Definitions for InitTask */

osThreadId_t InitTaskHandle;

const osThreadAttr_t InitTask_attributes = {

  .name = "InitTask",

  .stack_size = 256 * 4,

  .priority = (osPriority_t) osPriorityHigh,

};

/* Definitions for Print_CPU */

osThreadId_t Print_CPUHandle;

const osThreadAttr_t Print_CPU_attributes = {

  .name = "Print_CPU",

  .stack_size = 256 * 4,

  .priority = (osPriority_t) osPriorityHigh,

};



/* Private function prototypes -----------------------------------------------*/

/* USER CODE BEGIN FunctionPrototypes */



/* USER CODE END FunctionPrototypes */



void StartDefaultTask(void *argument);

void MyTask2(void *argument);

void MyTask3(void *argument);

void MyTask1(void *argument);

void MyTask4(void *argument);

void MyInitTask(void *argument);

void StartTask07(void *argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* Hook prototypes */

void configureTimerForRunTimeStats(void);

unsigned long getRunTimeCounterValue(void);

/* USER CODE BEGIN 1 */

/* Functions needed when configGENERATE_RUN_TIME_STATS is on */

__weak void configureTimerForRunTimeStats(void)

{

  CPU_RunTime=0;

}

__weak unsigned long getRunTimeCounterValue(void)

{

  return CPU_RunTime;

}

/* USER CODE END 1 */



/**

  * @brief  FreeRTOS initialization

  * @param  None

  * @retval None

  */

void MX_FREERTOS_Init(void) {

  /* USER CODE BEGIN Init */

   sys_cache_enable();

   HAL_UART_Transmit(&huart1,(uint8_t*)"HAL_TIM_Base_Start_IT \r\n", 20, HAL_MAX_DELAY);

   HAL_TIM_Base_Start_IT(&htim4);

   OLED_Init();

   OLED_Clear();

   HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);

  __HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_1, 0);

  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* creation of LED_PLAY_Task */

  LED_PLAY_TaskHandle = osThreadNew(MyTask2, NULL, &LED_PLAY_Task_attributes);

  /* creation of MUSIC_PLAY_Task */

  MUSIC_PLAY_TaskHandle = osThreadNew(MyTask3, NULL, &MUSIC_PLAY_Task_attributes);

  /* creation of OLED_SendBuffer */

  OLED_SendBufferHandle = osThreadNew(MyTask1, NULL, &OLED_SendBuffer_attributes);

  /* creation of EXIT_CHECK */

  EXIT_CHECKHandle = osThreadNew(MyTask4, NULL, &EXIT_CHECK_attributes)

  /* creation of InitTask */

  InitTaskHandle = osThreadNew(MyInitTask, NULL, &InitTask_attributes);

  /* creation of Print_CPU */

  Print_CPUHandle = osThreadNew(StartTask07, NULL, &Print_CPU_attributes);

  /* USER CODE BEGIN RTOS_THREADS */

  Exit_Queue = xQueueCreate(1,2);

  xSemaphore = xSemaphoreCreateBinary();

  if(xSemaphore == NULL)

  {

    while(1)

    {

      printf("error");/* 没有创建成功,用户可以在这里加入创建失败的处理机制 */

      HAL_Delay(100);

    }

  }

  /* 先释放一次,将初始值改为 1,利用二值信号量实现互斥功能 */

  xSemaphoreGive(xSemaphore);

}



/* USER CODE BEGIN Header_StartDefaultTask */

/**

  * @brief  Function implementing the defaultTask thread.

  * @param  argument: Not used

  * @retval None

  */

/* USER CODE END Header_StartDefaultTask */

void StartDefaultTask(void *argument)

{

  /* USER CODE BEGIN StartDefaultTask */

  /* Infinite loop */

  for(;;)

  {

      portYIELD_WITHIN_API();

//    osDelay(1);

  }

  /* USER CODE END StartDefaultTask */

}



/* USER CODE BEGIN Header_MyTask2 */

/**

* @brief Function implementing the LED_PLAY_Task thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_MyTask2 */

void MyTask2(void *argument)

{

  /* USER CODE BEGIN MyTask2 */

  /* Infinite loop */

  for(;;)

  {

    led_play();

    osDelay(1);

  }

  /* USER CODE END MyTask2 */

}



/* USER CODE BEGIN Header_MyTask3 */

/**

* @brief Function implementing the MUSIC_PLAY_Task thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_MyTask3 */

void MyTask3(void *argument)

{

  /* USER CODE BEGIN MyTask3 */

  static cnt =0;

  /* Infinite loop */

  for(;;)

  {

    music_play();

    if(cnt<3)

    {

      cnt++;

    }

    else{

      cnt = 0;

      osDelay(1);

    }

  }

  /* USER CODE END MyTask3 */

}



/* USER CODE BEGIN Header_MyTask1 */

/**

* @brief Function implementing the OLED_SendBuffer thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_MyTask1 */

void MyTask1(void *argument)

{

  /* USER CODE BEGIN MyTask1 */

  /* Infinite loop */

  for(;;)

  {

    int j=0;

    j++;

    u8g2_SendBuffer(&u8g2);

    if (func_index != last_index)

    {

      current_operation_index = table[func_index].current_operation;//执行当前操作函数    

     

      u8g2_ClearBuffer(&u8g2);

      (*current_operation_index)();              

      u8g2_SendBuffer(&u8g2);

     

      last_index = func_index;

    }

    xSemaphoreTake(xSemaphore, 5);//等待互斥量            

    switch(music_playflag)

    {

      case 0:buzzer_off();

    }

    switch(led_playflag)

    {

      case 0:LEDOFF;

    }

    xSemaphoreGive(xSemaphore);

    osDelay(4);//根据CPU占用率统计知这个任务占用高达90%改良后占60%

  }

  /* USER CODE END MyTask1 */

}



/* USER CODE BEGIN Header_MyTask4 */

/**

* @brief Function implementing the EXIT_CHECK thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_MyTask4 */

void MyTask4(void *argument)

{

//line1:  /* USER CODE BEGIN MyTask4 */

  uint16_t GPIO_Pin;

  static Pin4_cnt=0;

  /* Infinite loop */

  for(;;)

  {  

    if(xQueueReceive(Exit_Queue,&GPIO_Pin,100)==pdTRUE)

    {

      int cnt;

      cnt = 0;

      line1:

      while(HAL_GPIO_ReadPin(KEY1_GPIO_Port,GPIO_Pin) == RESET)

      {

        cnt++;

        osDelay(10);

      }

      if(cnt<3)

      {

        vTaskSuspend(EXIT_CHECKHandle);

        goto line1;

      }

      switch(GPIO_Pin)

      {

          case GPIO_PIN_1:

              func_index = table[func_index].up;// 向上翻

          break;

          case GPIO_PIN_2:

              func_index = table[func_index].down; //向下翻

          break;

          case GPIO_PIN_3:

              func_index = table[func_index].enter;//确认    

          break;

          case GPIO_PIN_4:

            Pin4_cnt++;

            if(Pin4_cnt == 1)

            {

              vTaskDelete( Print_CPUHandle );

            }

            if(Pin4_cnt >1)

            {

              Pin4_cnt = 0;

              taskENTER_CRITICAL();   /* 进入临界区 */      

              Print_CPUHandle = osThreadNew(StartTask07, NULL, &Print_CPU_attributes);

              taskEXIT_CRITICAL();      /* 退出临界区 */

            }

          break;

      }

    }

    vTaskSuspend(EXIT_CHECKHandle);

   

  }

  /* USER CODE END MyTask4 */

}

/* USER CODE BEGIN Header_MyInitTask */

/**

* @brief Function implementing the InitTask thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_MyInitTask */

void MyInitTask(void *argument)

{

  /* USER CODE BEGIN MyInitTask */

  /* Infinite loop */

  u8g2Init(&u8g2);//          

  u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);//要用创建任务式解决问题 此处延时有问题

  u8g2_ClearBuffer(&u8g2);

  vTaskSuspend(LED_PLAY_TaskHandle);//挂起任务        

  vTaskSuspend(MUSIC_PLAY_TaskHandle);//挂起任务          

  vTaskSuspend(EXIT_CHECKHandle);//挂起任务        

  vTaskDelete(InitTaskHandle);//删除初始化任务

  /* USER CODE END MyInitTask */

}



/* USER CODE BEGIN Header_StartTask07 */

/**

* @brief Function implementing the Print_CPU thread.

* @param argument: Not used

* @retval None

*/

/* USER CODE END Header_StartTask07 */

void StartTask07(void *argument)

{

  /* USER CODE BEGIN StartTask07 */

  char *pbuffer = (char *)malloc(1, 1024);

  char *qbuffer = (char *)malloc(1, 1024);

  /* Infinite loop */

  for(;;)

  {

    vTaskGetRunTimeStats(pbuffer);

    printf("Task name Run Count usage\r\n");

    printf("%s", pbuffer);

    osDelay(600);  

    vTaskList(qbuffer);

    printf("Task Task status Priority Number of the remaining stack task\r\n");

    printf("%s", qbuffer);

    free(pbuffer);

    free(qbuffer);

    osDelay(600);

  }

  /* USER CODE END StartTask07 */

}

通过挂起(唤醒)任务和任务阻塞延时以及任务优先级实现极佳的任务调度,此外通过消息队列和单任务内全局变量实现稳定的通信效果

    1. 多级菜单实现

采用较为简单的数组法(缺点是不易于增加菜单项)具体代码实现:

#include "driver_oled.h"

//#include "ascii_font.c"

#include "MENU.H"

#include "stm32h7xx.h"

#include "OLED_APP.H"

#include "freertos.h"

#include "main.h"

#include "PERSONAL_FONT.H"



volatile char music_flag = 0,music_playflag = 0,music_playflag_pre = 0,playflag = 0;;

volatile char led_flag = 0,led_playflag = 0,led_play_use = 0;

key_table table[]=

{

    //第0层

    {0,0,0,1,(*fun_0)},

   

    //第1层 流水灯 音乐 返回

    {1,3,2,4,(*fun_a1)},  

    {2,1,3,8,(*fun_b1)},

    {3,2,1,0,(*fun_c1)},        

   

    //第2层 一、二、三动画 返回

    {4,7,5,11,(*fun_a21)},                  

    {5,4,6,11,(*fun_a22)},

    {6,5,7,11,(*fun_a23)},                                          

    {7,6,4, 1,(*fun_a24)},

    //一、二音乐 返回

    {8,10,9,14,(*fun_b21)},                

    {9,8,10,14,(*fun_b22)},

    {10,9,8,2,(*fun_b23)},          

    //第3层      

    //播放 暂停

    //动画

    {11,13,12,17,(*fun_a2play)},

    {12,11,13,18,(*fun_a2stop)},

    {13,12,11,5,(*fun_a2return)},  

    //音乐

    {14,16,15,19,(*fun_b2play)},    

    {15,14,16,20,(*fun_b2stop)},    

    {16,15,14,9,(*fun_b2return)},      

    //第四层 应用层

    {17,11,11,11,(*fun_a2play_app)},

    {18,11,11,12,(*fun_a2stop_app)},

   

    {19,14,14,14,(*fun_b2play_app)},

    {20,14,14,15,(*fun_b2stop_app)},

};



/*********第0层***********/

void fun_0()

{

    u8g2_DrawStr(&u8g2,10,16,"QJC RTOS MP3");

    // u8g2_DrawStr(&u8g2,10,32,"QJC MP3 RTOS");

    // // u8g2_DrawStr(&u8g2,10,48,"");

    u8g2_DrawStr(&u8g2,52,32,"Enter to");  

    u8g2_DrawStr(&u8g2,68,48,"Start");  

    u8g2_DrawXBM(&u8g2,0, 16, 48, 48, LUNA);    

}

/*********第1层***********/

void fun_a1()  

{  

    u8g2_DrawStr(&u8g2,0,16,">");

    u8g2_DrawStr(&u8g2,24,16,"waterfall light");

    u8g2_DrawStr(&u8g2,24,32,"Music");

    u8g2_DrawStr(&u8g2,24,48,"return");

    u8g2_SetFont(&u8g2,u8g2_font_iconquadpix_m_all);

    u8g2_DrawGlyph(&u8g2,8,16, 0x76);/* dec 0x3032 电视图标  */

    u8g2_DrawGlyph(&u8g2,8,32, 0x69);/* dec 0x69 音乐图标  */

    u8g2_DrawGlyph(&u8g2,8,48,0x6E);/* dec 0x6E 返回图标  */        

    u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);                                                                          

}



void fun_b1()  

{  

    u8g2_DrawStr(&u8g2,0,32,">");

    u8g2_DrawStr(&u8g2,24,16,"waterfall light");

    u8g2_DrawStr(&u8g2,24,32,"Music");

    u8g2_DrawStr(&u8g2,24,48,"return");

    u8g2_SetFont(&u8g2,u8g2_font_iconquadpix_m_all);

    u8g2_DrawGlyph(&u8g2,8,16, 0x76);/* dec 0x3032 电视图标  */

    u8g2_DrawGlyph(&u8g2,8,32, 0x69);/* dec 0x69 音乐图标  */

    u8g2_DrawGlyph(&u8g2,8,48,0x6E);/* dec 0x6E 返回图标  */        

    u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);      

}



void fun_c1()    

{  

    u8g2_DrawStr(&u8g2,0,48,">");

    u8g2_DrawStr(&u8g2,24,16,"waterfall light");

    u8g2_DrawStr(&u8g2,24,32,"Music");

    u8g2_DrawStr(&u8g2,24,48,"return");        

    u8g2_SetFont(&u8g2,u8g2_font_iconquadpix_m_all);

    u8g2_DrawGlyph(&u8g2,8,16, 0x76);/* dec 0x3032 电视图标  */

    u8g2_DrawGlyph(&u8g2,8,32, 0x69);/* dec 0x69 音乐图标  */

    u8g2_DrawGlyph(&u8g2,8,48,0x6E);/* dec 0x6E 返回图标  */        

    u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);  

}

/*********第2层***********/

void fun_a21()    

{  

    u8g2_DrawStr(&u8g2,0,16,">");

    u8g2_DrawStr(&u8g2,16,16,"cartoon 1");

    u8g2_DrawStr(&u8g2,16,32,"cartoon 2");

    u8g2_DrawStr(&u8g2,16,48,"cartoon 3");

    u8g2_DrawStr(&u8g2,16,64,"return");    

  led_flag = 1;

}



void fun_a22()      

{  

    u8g2_DrawStr(&u8g2,0,32,">");

    u8g2_DrawStr(&u8g2,16,16,"cartoon 1");

    u8g2_DrawStr(&u8g2,16,32,"cartoon 2");

    u8g2_DrawStr(&u8g2,16,48,"cartoon 3");

    u8g2_DrawStr(&u8g2,16,64,"return");        

  led_flag = 2;    

}



void fun_a23()    

{  

    u8g2_DrawStr(&u8g2,0,48,">");

    u8g2_DrawStr(&u8g2,16,16,"cartoon 1");

    u8g2_DrawStr(&u8g2,16,32,"cartoon 2");

    u8g2_DrawStr(&u8g2,16,48,"cartoon 3");

    u8g2_DrawStr(&u8g2,16,64,"return");    

    led_flag = 3;      

}

void fun_a24()    

{  

    u8g2_DrawStr(&u8g2,0,64,">");

    u8g2_DrawStr(&u8g2,16,16,"cartoon 1");

    u8g2_DrawStr(&u8g2,16,32,"cartoon 2");

    u8g2_DrawStr(&u8g2,16,48,"cartoon 3");

    u8g2_DrawStr(&u8g2,16,64,"return");

}



void fun_b21()    

{  

    u8g2_DrawStr(&u8g2,0,16,">");

    u8g2_DrawStr(&u8g2,16,16,"Wind Rises");

    u8g2_DrawStr(&u8g2,16,32,"Solitary brave");

    u8g2_DrawStr(&u8g2,16,48,"return");

    music_flag = 1;//第一首音乐                                       

}

void fun_b22()    

{  

  u8g2_DrawStr(&u8g2,0,32,">");

    u8g2_DrawStr(&u8g2,16,16,"Wind Rises");

    u8g2_DrawStr(&u8g2,16,32,"Solitary brave");

    u8g2_DrawStr(&u8g2,16,48,"return");

    music_flag = 2;//第二首音乐

}

void fun_b23()    

{  

  u8g2_DrawStr(&u8g2,0,48,">");

    u8g2_DrawStr(&u8g2,16,16,"Wind Rises");

    u8g2_DrawStr(&u8g2,16,32,"Solitary brave");

    u8g2_DrawStr(&u8g2,16,48,"return");                                                                                    

}

void fun_a2play()    

{  

    u8g2_DrawStr(&u8g2,0,16,">");

    u8g2_DrawStr(&u8g2,24,16,"play");

    u8g2_DrawStr(&u8g2,24,32,"stop");

    u8g2_DrawStr(&u8g2,24,48,"return");

    u8g2_SetFont(&u8g2,u8g2_font_iconquadpix_m_all);    

  u8g2_DrawGlyph(&u8g2,8,16,0x54);/* dec 0x45 播放图标  */  

    u8g2_DrawGlyph(&u8g2,8,32,0x45);/* dec 0x45 暂停图标  */

    u8g2_DrawGlyph(&u8g2,8,48,0x6E);/* dec 0x6E 返回图标  */        

    u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);  

}



void fun_a2stop()    

{  

    u8g2_DrawStr(&u8g2,0,32,">");

    u8g2_DrawStr(&u8g2,24,16,"play");

    u8g2_DrawStr(&u8g2,24,32,"stop");

    u8g2_DrawStr(&u8g2,24,48,"return");

    u8g2_SetFont(&u8g2,u8g2_font_iconquadpix_m_all);    

  u8g2_DrawGlyph(&u8g2,8,16,0x54);/* dec 0x45 播放图标  */  

    u8g2_DrawGlyph(&u8g2,8,32,0x45);/* dec 0x45 暂停图标  */

    u8g2_DrawGlyph(&u8g2,8,48,0x6E);/* dec 0x6E 返回图标  */        

    u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);  

}

void fun_a2return()    

{  

    u8g2_DrawStr(&u8g2,0,48,">");

    u8g2_DrawStr(&u8g2,24,16,"play");

    u8g2_DrawStr(&u8g2,24,32,"stop");

    u8g2_DrawStr(&u8g2,24,48,"return");

    u8g2_SetFont(&u8g2,u8g2_font_iconquadpix_m_all);    

  u8g2_DrawGlyph(&u8g2,8,16,0x54);/* dec 0x45 播放图标  */  

    u8g2_DrawGlyph(&u8g2,8,32,0x45);/* dec 0x45 暂停图标  */

    u8g2_DrawGlyph(&u8g2,8,48,0x6E);/* dec 0x6E 返回图标  */        

    u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);  

}



void fun_b2play()    

{  

    u8g2_DrawStr(&u8g2,0,16,">");

    u8g2_DrawStr(&u8g2,24,16,"play");

    u8g2_DrawStr(&u8g2,24,32,"stop");

    u8g2_DrawStr(&u8g2,24,48,"return");

    u8g2_SetFont(&u8g2,u8g2_font_iconquadpix_m_all);    

  u8g2_DrawGlyph(&u8g2,8,16,0x54);/* dec 0x45 播放图标  */  

    u8g2_DrawGlyph(&u8g2,8,32,0x45);/* dec 0x45 暂停图标  */

    u8g2_DrawGlyph(&u8g2,8,48,0x6E);/* dec 0x6E 返回图标  */        

    u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);  

}

void fun_b2stop()    

{  

    u8g2_DrawStr(&u8g2,0,32,">");

    u8g2_DrawStr(&u8g2,24,16,"play");

    u8g2_DrawStr(&u8g2,24,32,"stop");

    u8g2_DrawStr(&u8g2,24,48,"return");

    u8g2_SetFont(&u8g2,u8g2_font_iconquadpix_m_all);    

  u8g2_DrawGlyph(&u8g2,8,16,0x54);/* dec 0x45 播放图标  */  

    u8g2_DrawGlyph(&u8g2,8,32,0x45);/* dec 0x45 暂停图标  */

    u8g2_DrawGlyph(&u8g2,8,48,0x6E);/* dec 0x6E 返回图标  */        

    u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);  

}

void fun_b2return()    

{  

    u8g2_DrawStr(&u8g2,0,48,">");

    u8g2_DrawStr(&u8g2,24,16,"play");

    u8g2_DrawStr(&u8g2,24,32,"stop");

    u8g2_DrawStr(&u8g2,24,48,"return");

    u8g2_SetFont(&u8g2,u8g2_font_iconquadpix_m_all);    

  u8g2_DrawGlyph(&u8g2,8,16,0x54);/* dec 0x45 播放图标  */  

    u8g2_DrawGlyph(&u8g2,8,32,0x45);/* dec 0x45 暂停图标  */

    u8g2_DrawGlyph(&u8g2,8,48,0x6E);/* dec 0x6E 返回图标  */        

    u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);  

}




void fun_a2play_app()    

{  

    u8g2_DrawStr(&u8g2,8,48,"ANY KEY RETURN");  

    u8g2_SetFont(&u8g2,u8g2_font_emoticons21_tr);  

  u8g2_DrawGlyph(&u8g2,50,24,0x36);/* dec 微笑图标  */  

    u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);  

    led_play_use = led_flag;

//  led_playflag = led_flag;

    vTaskResume(LED_PLAY_TaskHandle);//恢复任务

}



void fun_a2stop_app()    

{  

    u8g2_DrawStr(&u8g2,8,48,"ANY KEY RETURN");  

    u8g2_SetFont(&u8g2,u8g2_font_emoticons21_tr);  

  u8g2_DrawGlyph(&u8g2,50,24,0x35);/* dec 笑图标  */  

    u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);  

    led_play_use = 0;

    led_playflag = 0;

    vTaskSuspend(LED_PLAY_TaskHandle);

//  led_play();

}



void fun_b2play_app()    

{  

    u8g2_DrawStr(&u8g2,8,48,"ANY KEY RETURN");  

    u8g2_SetFont(&u8g2,u8g2_font_emoticons21_tr);  

    u8g2_DrawGlyph(&u8g2,50,24,0x36);/* dec 微笑图标  */    

    u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);  

    playflag = music_flag;

//  music_playflag = music_flag;//确认播放

    vTaskResume(MUSIC_PLAY_TaskHandle);

//  music_play();//此处不直接调用歌曲是为了方便FREERTOS的部署

}



void fun_b2stop_app()    

{  

    u8g2_DrawStr(&u8g2,8,48,"ANY KEY RETURN");  

    u8g2_SetFont(&u8g2,u8g2_font_emoticons21_tr);  

  u8g2_DrawGlyph(&u8g2,50,24,0x35);/* dec 笑图标  */  

    u8g2_SetFont(&u8g2,u8g2_font_DigitalDiscoThin_tf);

    playflag = 0;  

    music_playflag = 0;

    vTaskSuspend(MUSIC_PLAY_TaskHandle);

}

APP

#include "driver_oled.h"

#include "stm32h7xx.h"

#include "menu.h"

#include "LED.h"

#include "buzzed.h"

void music_play(void)

{

    music_playflag_pre = music_playflag;//更新

    music_playflag = music_flag;

    switch(music_playflag)

    {

//      case 0: buzzer_off();break;//放在OLED更新函数进行

        case 1: Wind_Rises();break;

        case 2: Solitary_brave();break;

    }

}

void led_play(void)

{

    xSemaphoreTake(xSemaphore, 5);//等待互斥量

    led_playflag = led_flag;        

    switch(led_playflag)

    {

//      case 0:LEDOFF;break;

        case 1:led_cartoon1();break;

        case 2:led_cartoon2();break;

        case 3:led_cartoon3();break;

        default:xSemaphoreGive(xSemaphore);//释放互斥量

    }

}
    1. 中断信息传输

由于在 FREERTOS 里中断是一种特别的形式,需调用专门的API 函数,并且通信比较复杂,需要使用队列以下是核心代码:最优化按键消抖:使用外部中断后唤醒相应任务函数,在任务函数里调用OSDELAY进行消抖,此举比在中断内延时提高了中断响应,比定时器扫描减少了资源占用,提高了CPU运行效率。

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

{

  static uint16_t GPIO_PIN_USE;

    //确保是否产生了EXTI Line中断

  uint32_t ulReturn;

  //  /* 进入临界段,临界段可以嵌套 */

  ulReturn = taskENTER_CRITICAL_FROM_ISR();

  GPIO_PIN_USE = GPIO_Pin;

  xQueueOverwriteFromISR(Exit_Queue,&GPIO_PIN_USE,&pxHigherPriorityTaskWoken);

  xTaskResumeFromISR(EXIT_CHECKHandle);//任务恢复

      //如果需要的话进行一次任务切换

    /* 退出临界段 */

  taskEXIT_CRITICAL_FROM_ISR( ulReturn );

}
    1. 外部中断原理

外部中断的功能可以配置六个寄存器;

中断屏蔽寄存器(EXTI_IMR)

事件屏蔽寄存器(EXTI_EMR)

上升沿触发选择寄存器(EXTI_RTSR)

下降沿触发选择寄存器(EXTI_FTSR)

软件中断事件寄存器(EXTI_SWIER)

挂起寄存器(EXTI_PR)

EXTI支持配置20个中断和事件屏蔽位;

GPIO端口以下图的方式连接到16个外部中断/事件线上;EXTI_Line0 — EXTI_Line15;

EXTI_Line16 连接到PVD输出 ;

EXTI_Line17连接到RTC闹钟事件;

EXTI_Line18连接到USB唤醒事件;

EXTI_Line19连接到以太网唤醒事件(只适用于互联型产品);

EXTI的配置,EXTI_Trigger这里支持三种模式;

EXTI_Trigger_Rising 上升沿触发;

EXTI_Trigger_Falling 下降沿触发;

EXTI_Trigger_Rising_Falling 上升沿和下降沿都可以触发;

寄存器的操作

以下摘自《STM32参考手册》

产生中断的步骤,必须先配置好并使能中断线。根据需要的边沿检测设置2个触发寄存器,同时在**中断屏蔽寄存器(EXTI_IMR)的相应位写1允许中断请求。当外部中断线上发生了期待的边沿时,将产生一个中断请求,对应的挂起位也随之被置1。在挂起寄存器(EXTI_PR)的对应位写1,将清除该中断请求。

产生事件的步骤:必须先配置好并使能事件线。根据需要的边沿检测通过设置2个触发寄存器,同时在中断屏蔽寄存器(EXTI_IMR)**的相应位写1允许事件请求。当事件线上发生了需要的边沿时,将产生一个事件请求脉冲,对应的挂起位不被置1。通过在软件中断/事件寄存器写1,也可以通过软件产生中断/事件请求。

中断屏蔽寄存器(EXTI_IMR)

事件屏蔽寄存器(EXTI_EMR)

上升沿触发选择寄存器(EXTI_RTSR)

下降沿触发选择寄存器(EXTI_FTSR)

软件中断事件寄存器(EXTI_SWIER)

挂起寄存器(EXTI_PR)

STM32H7 的每个 IO 都可以作为外部中断的中断输入口

每个PIN 有一个外部中断线,两个相同的 PIN 号不可同时外部中断

以线 0 为例: 它对应了 GPIOA.0、 GPIOB.0、 GPIOC.0、 GPIOD.0、 GPIOE.0、GPIOF.0、 GPIOG.0,GPIOH.0,GPIOI.0,GPIOJ.0,GPIOK.0。而中断线每次只

能连接到 1 个 IO 口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。

一般步骤

使能 IO 口时钟

设置 IO 口模式,触发条件,开启 SYSCFG 时钟,设置 IO

口与中断线的映射关系

因为我们这里初始化的是 PA0,调用该函数后中断线 0 会自动连接到 PA0。如果某个时间,我们又同样的方式初始化了PB0,那么 PA0 与中断线的链接将被清除,而直接链接 PB0到中断线 0。

配置中断优先级( NVIC),并使能中断。

设置好中断线和 GPIO 映射关系,然后又设置好了中断的触发模式等初始化参数。既然是外部中断,涉及到中断我们当然还要设置 NVIC 中断优先级。这个在前面已经讲解过,这里我们就接着上面的范例, 设置中断线 0 的中断优先级并使能外部中断 0 的方法为:

HAL_NVIC_SetPriority(EXTI0_IRQn,2,1); //抢占优先级为 2,子优先级为 1 HAL_NVIC_EnableIRQ(EXTI0_IRQn); //使能中断线 2

 

编写中断服务函数。

中断服务函数的名字是在 HAL 库中事先有定义的。这里需要说明一下, STM32H7 的 IO 口外部中断函数只有 7 个,分别为:

void EXTI0_IRQHandler(); void EXTI1_IRQHandler(); void EXTI2_IRQHandler(); void EXTI3_IRQHandler(); void EXTI4_IRQHandler(); void EXTI9_5_IRQHandler(); void EXTI15_10_IRQHandler();

中断线 0-4 每个中断线对应一个中断函数,中断线 5-9 共用中断函数EXTI9_5_IRQHandler,中断线 10-15 共用中断函数 EXTI15_10_IRQHandler

编写中断处理回调函数 HAL_GPIO_EXTI_Callback

在使用 HAL 库的时候,我们也可以跟使用标准库一样,在中断服务函数中编写控制逻辑。但 是 HAL 库 为 了 用 户 使用 方 便 , 它 提 供 了 一 个 中 断 通 用 入 口 函 数 HAL_GPIO_EXTI_IRQHandler,在该函数内部直接调用回调函数 HAL_GPIO_EXTI_Callback。

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)

{

if( HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)

{

 HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);

HAL_GPIO_EXTI_Callback(GPIO_Pin);

}

}

 

在所有的外部中断服务函数中直接调用外部中断共用处理函数 HAL_GPIO_EXTI_IRQHandler,然后在回调函数 HAL_GPIO_EXTI_Callback 中通过判断中断是来自哪个 IO口编写相应的中断服务控制逻辑。

如果是程序运行期间的引脚状态切换,最好采用下面的方式或者直接寄存器操作:

GPIO_InitStruct.Pin = GPIO_PIN_0 |GPIO_PIN_1 | GPIO_PIN_2 ; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //这里会执行 16 for 查询

然后在 NVIC 配置里使能中断

HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);

HAL_GPIO_EXTI_Callback(GPIO_PIN_4);

第三章 仿真现象与结果讨论

    1. 仿真现象

开始界面按下 KEY1 进入一级菜单

按下向下键实现功能选择,改变函数指针并刷新屏幕

在流水灯选项按下确认

一样可以选择想要的动画,选择好后按确定进入选择播放,此时会读取按键信息并更新显示,执行变化后的函数指针的函数,同时唤醒相应任务

此时最下方 LED 点亮

  1. 音乐选项同理

可以看出此项目与 MP3 功能相近,实际上经过长达两天的 DEBUG 已经使整个稳定性达到较高水准,该项目可以用于新能源汽车的多媒体中心,也可以用于随身听,此外,该项目也可以用于文艺汇演的灯光音响控制,其 FREERTOS 的调度使其易于移植于需要交互的对及时性要求高的地方,如电机控制显示

结  语

在长达一个星期的写代码,DEBUG 后发现了一些问题,比如播放一个音乐暂停再播放会卡住很久,比如音乐播放直接卡死,又比如按键按下对其他任务有影响,又比如播放一个音乐不暂停直接播放另外一首,又比如在播放音乐的时候播放动画是否会改变动画效果(LED 的闪烁时间),但是这些问题都被我一一解决了,这也是我第一次用 FREERTOS 写较大型的应用程序,也是第一次写多级菜单,用图形库,使用 HAL 库和 CUBEMX 进行较大项目开发。

其中不得不提 PROTEUS 仿真实在是糟糕,很多硬件上有效果的代码仿真不出来,不支持硬件 I2C 是大问题,此外还在图形显示方面有一些问题,不能显示中文,开机不可以清屏,按 RESET 会大概率直接卡死。不过图片界面和 UI 界面都在实物上有效显示。

总的回顾起来还是很高兴能够实现这个项目,该项目已经比较完善,但是在以下几个地方可以提升:1.使用 RGB 触摸屏增强交互性与显示效果 2.采用 ENWIN 进行界面绘制 3.使用动画进行切换 4.多级菜单采用链表结构 5.对于实时性要求不高的系统应用改用LINUX 系统6.增加MP3和WAV格式的解码,加入FATFS文件管理系统

随着个人技术提升与精进相信可以更好的改良该项目。

  • 28
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值