我是Free RTOS的新手,我按照一些教程一行一行地做,但事情没有总结正确,我用free RTOS来切换3个LEDs,但它只点亮了其中的2个,没有切换!随机的2个LED,无论我改变优先级或切换的延迟时间,随机的2个LED只是打开,没有更多的东西,我在proteus模拟和真实的硬件上尝试了代码,同样的问题存在,谁能帮我解决这个问题?
MC:ATMEGA32A
实时操作系统:FreeRTOS
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
/* FreeRTOS files. */
#include "FreeRTOS.h"
#include "task.h"
#include "croutine.h"
#include "FreeRTOSConfig.h"
/* Define all the tasks */
static void ledBlinkingtask1(void* pvParameters);
static void ledBlinkingtask2(void* pvParameters);
static void ledBlinkingtask3(void* pvParameters);
int main(void) {
/* Call FreeRTOS APIs to create tasks, all tasks has the same priority "1" with the
same stack size*/
xTaskCreate( ledBlinkingtask1,"LED1",
configMINIMAL_STACK_SIZE, NULL, 1, NULL );
xTaskCreate( ledBlinkingtask2,"LED2",
configMINIMAL_STACK_SIZE, NULL,1, NULL );
xTaskCreate( ledBlinkingtask3,"LED3",
configMINIMAL_STACK_SIZE, NULL,1, NULL );
// Start the RTOS kernel
vTaskStartScheduler();
/* Do nothing here and just run infinte loop */
while(1){};
return 0;
}
static void ledBlinkingtask1(void* pvParameters){
/* Define all variables related to ledBlinkingtask1*/
const uint8_t blinkDelay = 100 ;
/* make PB0 work as output*/
DDRB |= (1<<0); //PB0
/* Start the infinte task 1 loop */
while (1)
{
PORTB ^= (1<<0); //toggle PB0 //PB0
vTaskDelay(blinkDelay); //wait some time
}
}
static void ledBlinkingtask2(void* pvParameters){
/* Define all variables related to ledBlinkingtask2*/
const uint8_t blinkDelay = 100;
/* make PB1 work as output*/
DDRB |= (1<<1);//PB0
/* Start the infinte task 2 loop */
while (1)
{
PORTB ^= (1<<1); //toggle PB0 //PB0
vTaskDelay(blinkDelay); //wait some time
}
}
static void ledBlinkingtask3(void* pvParameters){
/* Define all variables related to ledBlinkingtask3*/
const uint16_t blinkDelay = 100;
/* make PB2 work as output*/
DDRB |= (1<<2); //PB2
/* Start the infinte task 3 loop */
while (1)
{
PORTB ^= (1<<2); //toggle PB0 //PB0
vTaskDelay(blinkDelay); //wait some time
}
}
ps:每个任务都能单独工作,但不能一起工作!
正如评论中已经提到的那样--主要问题似乎是访问驱动LEDs的端口寄存器既不
PORTB ^= (1<<0); // in task 1
[...]
PORTB ^= (1<<1); // in task 2
[...]
PORTB ^= (1<<2); // in task 3
每次使用C代码中的一条指令访问HW寄存器可能会引起误解.不过,这并没有什么帮助,因为编译器会生成几条汇编器指令(例如,将以前的端口值加载到寄存器,修改该寄存器值,将其写回端口)。这样一来,一个任务可以中断另一个任务 在这些汇编器CPU指令之间 几个任务依次向 port 写回 "他们 "的寄存器值,可能会使其他任务刚刚写到 port 的值恢复原样,因此您会错过一个(或几个)闪烁事件。
因此,解决的办法是互相保护分配.按照上面的编号顺序,这可能意味着以下任何一种情况。
PORTB
端口寄存器。如果是这样,那么向该端口写入一个位就会成为一个 原子 的方式来进行LED的切换。很抱歉,我不知道Atmega的硬件接口。也许,这是不可能的,你必须直接进入2.和3.a. 在改变端口寄存器之前禁用中断,之后重新启用。这样,任务调度器就不会在那段时间(=关键部分)运行,没有人打扰访问硬件的任务。
b. 使用 taskENTER_CRITICAL()
taskEXIT_CRITICAL()
c. 使用mutex或类似的方法。
创建第四个任务,它在邮箱队列中等待(阻塞).每当它收到一个来自邮箱的值时,它就会对其进行处理(例如,通过对端口寄存器进行XOR-ing).现有的三个任务本身并不访问LED端口寄存器,而是将这样的值(=请求消息)发送给新的任务.给新的任务分配一个较高的优先级,以获得一个平滑的闪烁模式。
如果选项1.在你的控制器上是可行的,那么它是最快的(但它需要硬件平台的某些功能...)。否则,我同意@Richard的提示,选项2.b.是最快的(2.a.同样快,但不是那么干净,因为你打破了FreeRTOS库的分层)。
选项2.c.可能会引入一个显著的开销,而选项3.是非常干净的,但在你的情况下是完全矫枉过正的。如果你的问题只是关于LED灯的闪烁 请把推土机留在车库里,选择方案2.