基于人机体感交互的助老助残轮椅系统设计
一、Atmega2560单片机——FreeRTOS版本
#include <Arduino_FreeRTOS.h>
#include <semphr.h>
#include <queue.h>
int PA = 8;
int PB = 2;
int Motor_A1 = 9;
int Motor_A2 = 10;
int Motor_B1 = 3;
int Motor_B2 = 4;
int Speed_Helmet = 40;
char inChar = 'x';
void taskSerialReceive(void *pvParameters);
void taskFollowGesture(void *pvParameters);
void taskManual(void *pvParameters);
QueueHandle_t xQueue;
SemaphoreHandle_t xSemaphore;
void setup()
{
Serial.begin(9600);
Serial1.begin(9600);
pinMode(PA, OUTPUT);
pinMode(PB, OUTPUT);
pinMode(Motor_A1, OUTPUT);
pinMode(Motor_A2, OUTPUT);
pinMode(Motor_B1, OUTPUT);
pinMode(Motor_B2, OUTPUT);
xQueue = xQueueCreate(5, sizeof(int) * 3);
xSemaphore = xSemaphoreCreateMutex();
xTaskCreate(taskSerialReceive, "SerialReceive", 128, NULL, 1, NULL);
xTaskCreate(taskFollowGesture, "FollowGesture", 128, NULL, 2, NULL);
xTaskCreate(taskManual, "Manual", 128, NULL, 2, NULL);
}
void taskSerialReceive(void *pvParameters) {
int buf[4];
while (true) {
if (Serial.available()) {
for (int i = 0; i < 4; i++) {
buf[i] = Serial.read();
}
if (xQueueSend(xQueue, (void *)&buf, portMAX_DELAY) != pdTRUE) {
Serial.println("Error: Queue full");
}
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void taskFollowGesture(void *pvParameters) {
int buf[4];
while (true) {
if (xQueueReceive(xQueue, (void *)&buf, portMAX_DELAY) == pdTRUE) {
if (buf[3] == 0) {
if (xSemaphoreTake(xSemaphore, (TickType_t)0xFFFF) == pdTRUE) {
Forward(buf[0], buf[1], buf[2]);
xSemaphoreGive(xSemaphore);
}
}
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void taskManual(void *pvParameters) {
int buf[4];
while (true) {
if (xQueueReceive(xQueue, (void *)&buf, portMAX_DELAY) == pdTRUE) {
if (buf[2] == 1) {
if (xSemaphoreTake(xSemaphore, (TickType_t)0xFFFF) == pdTRUE) {
if (Serial1.available())
{
inChar = Serial1.read();
Serial.println(inChar);
}
if(inChar == 'w')
Forward(Speed_Helmet, Speed_Helmet , 1);
if(inChar == 's')
Forward(Speed_Helmet, Speed_Helmet, 2);
if(inChar == 'a')
Forward(Speed_Helmet, Speed_Helmet, 3);
if(inChar == 'd')
Forward(Speed_Helmet, Speed_Helmet, 4);
if(inChar == 'x')
Forward(0,0,1);
xSemaphoreGive(xSemaphore);
}
}
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void loop() {
vTaskDelay(pdMS_TO_TICKS(1000)); // The loop function must never block
}
void Forward(int SpeedL, int SpeedR, int Direction)
{
if(Direction == 1)
{
digitalWrite(Motor_A1, HIGH);
digitalWrite(Motor_A2, LOW);
digitalWrite(Motor_B1, HIGH);
digitalWrite(Motor_B2, LOW);
analogWrite(PA, SpeedR);
analogWrite(PB, SpeedL);
}
else if(Direction == 2)
{
digitalWrite(Motor_A1, LOW);
digitalWrite(Motor_A2, HIGH);
digitalWrite(Motor_B1, LOW);
digitalWrite(Motor_B2, HIGH);
analogWrite(PA, SpeedR);
analogWrite(PB, SpeedL);
}
else if(Direction == 3)
{
digitalWrite(Motor_A1, LOW);
digitalWrite(Motor_A2, HIGH);
digitalWrite(Motor_B1, HIGH);
digitalWrite(Motor_B2, LOW);
analogWrite(PA, SpeedR);
analogWrite(PB, SpeedL);
}
else if(Direction == 4)
{
digitalWrite(Motor_A1, HIGH);
digitalWrite(Motor_A2, LOW);
digitalWrite(Motor_B1, LOW);
digitalWrite(Motor_B2, HIGH);
analogWrite(PA, SpeedR);
analogWrite(PB, SpeedL);
}
}
二、代码解析
01 队列底层原理
QueueHandle_t xQueue; // 定义队列对象
xQueue = xQueueCreate(5, sizeof(int) * 3); // 定义队列大小
xQueueSend(xQueue, (void *)&buf, portMAX_DELAY) //往队列尾部写入数据,如果没有空间,阻塞时间为portMAX_DELAY
xQueueReceive(xQueue, (void *)&buf, portMAX_DELAY) //从队列中读取队列项,完成以后删除掉队列项
底层原理
- 双向循环链表
- 阻塞唤醒机制
(1)发送数据 队列满 阻塞
(2)接收数据 队列空 阻塞
02 信号量底层原理
SemaphoreHandle_t xSemaphore;
xSemaphore = xSemaphoreCreateMutex();
xSemaphoreTake(xSemaphore, (TickType_t)0xFFFF)
xSemaphoreGive(xSemaphore)
底层原理
在FreeRTOS中,每个信号量都由一个结构体对象表示(Semaphore_t)。该对象包含以下成员:
-
uxSemaphoreCount:当前可用计数值。
-
xMutexHolder:持有互斥锁或二进制信号量所有权的任务句柄。
-
xTasksWaitingToSend:等待发送二进制信号量或计数型信号量上请求数据队列头指针。
-
xTasksWaitingToReceive:等待接收二进制信号量或计数型信号量上响应数据队列头指针。
当一个任务需要使用共享资源时,它会尝试获取该资源所关联的信号量。如果当前可用计数大于0,则该任务可以获得对共享资源的访问权限,并将可用计数减1。否则,如果当前无法获得该信号量,则该任务将被阻塞,并加入到等待发送/接收队列中。
当另一个任务释放了已经获取到并使用完毕的共享资源时,它会通过给与相应数量(通常为1)增加这个特定类型(Binary semaphore, Counting semaphore) 的 Semaphore 对象内部变化uxSemaphoreCount 来释放信号量。如果当前有任务在等待该信号量,则会从相应的队列中唤醒一个或多个任务,以便它们可以获取对共享资源的访问权限。总而言之,FreeRTOS的信号量是通过计数器实现的同步机制,用于控制对共享资源的访问。当某个任务需要使用共享资源时,它会尝试获取关联的信号量,并根据可用计数值来决定是否能够获得该信号量。如果无法获取,则该任务将被阻塞并加入到等待队列中;否则,该任务可以获得对共享资源的访问权限,并将可用计数减1。当另一个任务释放已经使用完毕的共享资源时,它会增加相应类型(Binary semaphore, Counting semaphore) 的Semaphore对象内部变化uxSemaphoreCount 来释放信号量,并唤醒正在等待这个特定类型 Semaphore 对象上请求数据队列头指针xTasksWaitingToSend 或者响应数据队列头指针xTasksWaitingToReceive 中任意一个或多个挂起状态下处于等待状态下面 的Task 。
03 任务的创建与切换(调度器)
xTaskCreate(taskSerialReceive, "SerialReceive", 128, NULL, 3, NULL);
xTaskCreate(taskFollowGesture, "FollowGesture", 128, NULL, 2, NULL);
xTaskCreate(taskManual, "Manual", 128, NULL, 2, NULL);
vTaskDelay(pdMS_TO_TICKS(10));
任务创建
当创建任务时,会把任务插入到对应优先级的ReadyList链表里去,同时把自己的TCB指针指向优先级最高的任务的TCB
任务调度策略
FreeRTOS 任务调度原理(基于cortexM内核)_freertos任务调度原理_夜逐天光的博客-CSDN博客
基本调度策略
- 抢占式调度
- 时间片轮转
默认情况下,FreeRTOS使用固定优先级抢占式调度策略,并对同等优先级的任务进行循环时间切片:
底层原理
list是是FreeRTOS核心的数据结构,它在list.c文件中实现为一个双向循环的链表。而FreeRTOS的抢占式的调度策略就是基于此实现的。
根据优先级设置链表数量,有几个优先级就有几个ReadyList(就绪链表)