助老助残轮椅系统设计——FreeRTOS版本程序

基于人机体感交互的助老助残轮椅系统设计

一、Atmega2560单片机——FreeRTOS版本

#include 
#include 
#include 


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(就绪链表)

你可能感兴趣的:(#,嵌入式MCU开发,单片机,c语言,嵌入式硬件)