这个是全网最详细的STM32项目教学视频。
第一篇在这里:
视频在这里
STM32智能小车V3-STM32入门教程-openmv与STM32循迹小车-stm32f103c8t6-电赛 嵌入式学习 PID控制算法 编码器电机 跟随
**V3:HAL库开发、手把手教学下面功能:PID速度控制、PID循迹、PID跟随、遥控、避障、PID角度控制、openmv视觉控制、电磁循迹、FreeRTOS、K210视觉智能车(更新中)、K230视觉智能车(更新中)、MSPM0G3507视觉智能车(更新中)**
22.2-任务的创建(有代码示例)
任务创建生成的代码
上一个章节我们创建了LED的任务这节我们分析一下相关代码,然后再创建一个OLED刷新也就是模式零的任务
可以看到STM32cubemx软件生成RTOS代码调用的CMSIS-RTOS的函数(以os开通的函数都是CMSIS-RTOS的函数)
我们很容易就找到函数原型其实是
函数原型:
函数描述:
函数 xTaskCreate 用于实现 FreeRTOS 操作系统的任务创建,并且还可以自定义任务栈的大小。
- 第 一 个参数填创建任务的函数名
- 第 二 个参数是任务名,这个参数主要用于调试的时候方便看是哪个任务
- 第 三 个参数是任务栈大小,单位 word,也就是 4 字节
- 第 四 个参数是创建的任务函数的形参
- 第 五 个参数是定义任务优先级
- 第 六 个参数是任务句柄,用于区分不同的任务
然后我们看一下,我们生成的代码都有那些
生成的的代码里面会定义任务的句柄
动手再创建一个任务
我们复制上一节21-2_LED_FreeRTOS文件 命名为22-2_LED_FreeRTOS
然后点击LED.ioc
我们这里再新建一个任务用于OLED刷新,也就是我们之前裸机时候的模式0大部分功能
由于OLED刷新的任务对实时性要求不高,所以我们优先级设置低一些
这里优先级设置低于正常优先级
然后点击生成代码,可以观察到已经生成新的代码,
这个就是我们OLED刷新任务对应的函数,我们在该函数中增加OLED刷新代码
将之前模式零的代码复制过去
//0LED显示功能
sprintf((char*)OledString, "V1:%.2fV2:%.2f", Motor1Speed,Motor2Speed);//显示速度
OLED_ShowString(0,0,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,
sprintf((char*)OledString, "Mileage:%.2f", Mileage);//显示里程
OLED_ShowString(0,1,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,
sprintf((char*)OledString, "U:%.2fV", adcGetBatteryVoltage());//显示电池电压
OLED_ShowString(0,2,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,
sprintf((char *)OledString,"HC_SR04:%.2fcm\r\n",HC_SR04_Read());//显示超声波数据
OLED_ShowString(0,3,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,
sprintf((char *)OledString,"p:%.2f r:%.2f \r\n",pitch,roll);//显示6050数据 俯仰角 横滚角
OLED_ShowString(0,4,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,
sprintf((char *)OledString,"y:%.2f \r\n",yaw);//显示6050数据 航向角
OLED_ShowString(0,5,OledString,12);//这个是oled驱动里面的,是显示位置的一个函数,
//蓝牙APP显示
sprintf((char*)Usart3String, "V1:%.2fV2:%.2f", Motor1Speed,Motor2Speed);//显示速度
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小
//阻塞方式发送可以保证数据发送完毕,中断发送不一定可以保证数据已经发送完毕才启动下一次发送
sprintf((char*)Usart3String, "Mileage:%.2f", Mileage);//显示里程
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小
sprintf((char*)Usart3String, "U:%.2fV", adcGetBatteryVoltage());//显示电池电压
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小
sprintf((char *)Usart3String,"HC_SR04:%.2fcm\r\n",HC_SR04_Read());//显示超声波数据
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小
sprintf((char *)Usart3String,"p:%.2f r:%.2f \r\n",pitch,roll);//显示6050数据 俯仰角 横滚角
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小
sprintf((char *)Usart3String,"y:%.2f \r\n",yaw);//显示6050数据 航向角
HAL_UART_Transmit(&huart3,( uint8_t *)Usart3String,strlen(( const char *)Usart3String),50);//阻塞式发送通过串口三输出字符 strlen:计算字符串大小
//获得6050数据
// while(mpu_dmp_get_data(&pitch,&roll,&yaw)!=0){} //这个可以解决经常读不出数据的问题
//在显示模式电机停转 设置小车速度为0
motorPidSetSpeed(0,0);
有很多错误,我们解决一些,freeRTOS.c增加头文件
#include "oled.h"
#include "stdio.h"
#include "motor.h"
#include "niming.h"
#include "pid.h"
#include "cJSON.h"
#include <string.h>
#include "HC_SR04.h"
#include "mpu6050.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
#include "adc.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
然后把main.c里面定义和声明的变量到Freertos.c中都声明一下, 主要声明的时候不要赋值
extern short Encode1Count ;//电机1编码器计数值 short的类型
extern short Encode2Count ;//电机2编码器计数值 short的类型
extern float Motor1Speed ;//电机1速度 转/s
extern float Motor2Speed ;//电机2速度 转/s
extern uint16_t TimerCount ;//中断计数变量
extern tPid pidMotor1Speed;//声明电机1PID速度控制结构体类型变量
extern tPid pidMotor2Speed;
extern tPid pidFollow; //定距离跟随PID
extern tPid pidMPU6050YawMovement; //利用6050偏航角 进行姿态控制的PID
extern uint8_t Usart1_ReadBuf[255]; //串口1 缓冲数组
extern float p,i,d,a,b;//使用JSON时候使用的变量
extern uint8_t OledString[50];//OLED显示使用的字符串数组
extern float Mileage;//里程数
extern tPid pidHW_Tracking;//红外循迹的PID
extern tPid pidOpenmv_Tracking;//摄像头的PID
extern uint8_t g_ucaHW_Read[4] ;//保存红外对管电平的数组
extern int8_t g_cThisState ;//这次状态
extern int8_t g_cLastState ; //上次状态
extern float g_fHW_PID_Out;//红外对管PID计算输出速度
extern float g_fHW_PID_Out1;//电机1的最后循迹PID控制速度
extern float g_fHW_PID_Out2;//电机2的最后循迹PID控制速度
extern uint8_t g_ucUsart3ReceiveData; //保存串口三接收的数据
extern uint8_t g_ucUsart2ReceiveData; //保存串口二接收的数据
extern uint8_t Usart3String[50];//串口三输出字符串使用的字符串数组
extern float g_fHC_SR04_Read;//超声波传感器读取障碍物数据
extern float g_fFollow_PID_Out;//定距离跟随PID计算输出速度
extern float pitch,roll,yaw; //俯仰角 横滚角 航向角
extern float g_fMPU6050YawMovePidOut ; //姿态PID运算输出
extern float g_fMPU6050YawMovePidOut1 ; //第一个电机控制输出
extern float g_fMPU6050YawMovePidOut2 ; //第一个电机控制输出
extern uint8_t g_ucMode ;
extern int g_lHW_State;//帮助视觉调试 用于表示红外对管或者视觉摄像头识别状态
编译一下就没有错误了,然后我们把代码烧录观察现象
下面能够观察到OLED屏幕在快速刷新,同时LED函数
注意如果注释掉MPU6050初始化和数据获取函数,那么OLED上是不会显示6050数据值的。
而且OLED刷新的任务中一定要有osDelay 确保任务能供定期进入阻塞状态,使得其他任务能得到调度。
修改一下延时时间