【FreeRTOS】事件组实验-改进姿态控制


0 前言

学习视频:
【FreeRTOS 入门与工程实践 --由浅入深带你学习 FreeRTOS(FreeRTOS 教程 基于 STM32,以实际项目为导向)】 【精准空降到 00:28】 https://www.bilibili.com/video/BV1Jw411i7Fz/?p=43&share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933&t=28

参考《FreeRTOS 入门与工程实践(基于 DshanMCU-103).pdf》

  • 《第 10 章 同步互斥与通信》
  • 《第 14 章 事件组(event group)》

1 事件组实验_改进姿态控制

本节源码:在"25_eventgroup_and"的基础上,改出:26_eventgroup_mpu6050

2 改进思路

以前的程序框图

在这里插入图片描述

  • 这里的思路不是使用中断来操作MPU6050,我们是创建了一个任务,读取I2C,但是读取I2C这个操作很耗时间,就需要放到任务里去完成,这没有问题的
  • 那我们怎么样让这个任务及时获得我们摇晃的动作呢?
  • 我们用的是vTaskDelay,使其50ms

改进思路如下:

  • 我们可以使用中断,在中断里唤醒任务,在任务里读取I2C,读到数据之后再写队列,把数据写到队列3去,这里会同时写队列集,这个InputTask就会获得队列3的句柄,然后读取队列3得到摇晃的数据,最后去控制游戏。
  • 现在我们需要增加一个中断在中断里怎么唤醒任务?
  • 我们可以用事件组来唤醒:在中断里设置事件,在读I2C之前等待事件(读不到事件就一直阻塞)

这样和之前的程序相比较,现在就少了很多无谓的操作了

在这里插入图片描述

2.1 创建事件

static EventGroupHandle_t g_xEventMPU6050;  //定义MPU6050事件


/* 然后再到MPU6050_Init创建事件 */
int MPU6050_Init(void)
{
	MPU6050_WriteRegister(MPU6050_PWR_MGMT_1, 0x00);	//解除休眠状态
	MPU6050_WriteRegister(MPU6050_PWR_MGMT_2, 0x00);
	MPU6050_WriteRegister(MPU6050_SMPLRT_DIV, 0x09);
	MPU6050_WriteRegister(MPU6050_CONFIG, 0x06);
	MPU6050_WriteRegister(MPU6050_GYRO_CONFIG, 0x18);
	MPU6050_WriteRegister(MPU6050_ACCEL_CONFIG, 0x18);

	g_xQueueMPU6050 = xQueueCreate(MPU6050_QUEUE_LEN, sizeof(struct mpu6050_data));
	g_xEventMPU6050 = xEventGroupCreate();	
	return 0;
}

2.2 等待事件

然后在任务里等待事件

void MPU6050_Task(void *params)
{	
    int16_t AccX;
	struct mpu6050_data result;
	int ret;

    extern void GetI2C(void);
    extern void PutI2C(void);
    while (1)
    {    
        
        /* 等待事件 */
        /* 等待bit0 and bit1事件 */
        xEventGroupWaitBits(g_xEventMPU6050, (1<<0),    // 等待bit0 and bit1事件
                            pdTRUE,                 //  pdTRUE: 清除uxBitsToWaitFor指定的位
                            pdTRUE,                 // 都为1才可以 
                            portMAX_DELAY);         // 一定等到成功才返回
        
		/* 读数据 */
        
        GetI2C();   // 使用LCD之前,先获得互斥量。会一直等待
        ret = MPU6050_ReadData(&AccX, NULL, NULL, NULL, NULL, NULL);
        PutI2C();   // 使用LCD之后,再释放互斥量
		
        if (0 == ret)
		{
			/* 解析数据 */
			MPU6050_ParseData(AccX, 0, 0, 0, 0, 0, &result);

			/* 写队列 */
			xQueueSend(g_xQueueMPU6050, &result, 0);
		}
		
		/* delay */
		vTaskDelay(50);
	}
}


加入必要的头文件

#include "FreeRTOS.h"                   // ARM.FreeRTOS::RTOS:Core
#include "task.h"                       // ARM.FreeRTOS::RTOS:Core
#include "event_groups.h"               // ARM.FreeRTOS::RTOS:Event Groups

2.3 设置事件

在这里插入图片描述
根据原理图,我们使用PB5的外部中断用来触发,设置事件。

MPU6050的INT引脚是外部中断引脚,触发中断是高电平信号,INT引脚静息状态为低电平。所以这里我们配置上升沿触发,上拉输入

在这里插入图片描述

在这里插入图片描述

void EXTI9_5_IRQHandler(void)
{
    /* 设置事件组:bit0 */
    xEventGroupSetBits(g_xEventMPU6050, (1<<0));
}

2.4 Debug

编译下载程序,发现不能运行,我们先来检查是否使能了中断,再检查是否进入了中断,打开CubeMX发现确实没使能中断,

在这里插入图片描述
然后就报错了,说我们重复定义了

01_freertos_template\01_freertos_template.axf:
 Error: L6200E: Symbol EXTI9_5_IRQHandler multiply defined 
 (by driver_mpu6050.o and stm32f1xx_it.o).

然后编写MPU6050的设置事件组的代码

void MPU6050_Callback(void)
{
    /* 设置事件组:bit0 */
    xEventGroupSetBitsFromISR(g_xEventMPU6050, (1<<0), NULL);   //在中断里设置事件
}

声明这个文件,并且在中断回调函数里添加 case GPIO_PIN_5的情况!

extern void MPU6050_Callback(void);


void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    switch (GPIO_Pin)
    {
        case GPIO_PIN_5:
        {
            MPU6050_Callback();
            break;
        }
        case GPIO_PIN_10:
        {
            IRReceiver_IRQ_Callback();
            break;
        }

        case GPIO_PIN_12:
        {
            RotaryEncoder_IRQ_Callback();
            break;
        }

        default:
        {
            break;
        }
    }
}

2.5 设置MPU6050寄存器

设置寄存器,做初始化工作:配置中断引脚使能中断

#define	MPU6050_INT_PIN_CFG		0x37    //引脚配置
#define	MPU6050_INT_ENABLE		0x38    //中断使能

int MPU6050_Init(void)
{
	MPU6050_WriteRegister(MPU6050_PWR_MGMT_1, 0x00);	//解除休眠状态
	MPU6050_WriteRegister(MPU6050_PWR_MGMT_2, 0x00);
	MPU6050_WriteRegister(MPU6050_SMPLRT_DIV, 0x09);
	MPU6050_WriteRegister(MPU6050_CONFIG, 0x06);
	MPU6050_WriteRegister(MPU6050_GYRO_CONFIG, 0x18);
	MPU6050_WriteRegister(MPU6050_ACCEL_CONFIG, 0x18);
    
    /* 配置中断引脚 */
    MPU6050_WriteRegister(MPU6050_INT_PIN_CFG, 0);
    /* 使能中断 */
    MPU6050_WriteRegister(MPU6050_INT_ENABLE, 0xFF);

	g_xQueueMPU6050 = xQueueCreate(MPU6050_QUEUE_LEN, sizeof(struct mpu6050_data));
	g_xEventMPU6050 = xEventGroupCreate();	//创建事件
	return 0;
}

烧录程序,现在就能正常运行了,但是运行的特别快,我们在MPU6050_Task的最后取消注释vTaskDelay(50);
现在就很丝滑了

3 总结

我们实现了在中断里使用事件组改造这个程序,这个任务只有当中断写了事件之后,得到了事件,才会读取I2C,避免了一直读取I2C的数据,节省资源。

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北国无红豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值