FreeRTOS не работает должным образом на Atmega32A - PullRequest
1 голос
/ 19 апреля 2020

Я новичок в бесплатной ОСРВ, и я построчно следовал некоторому учебнику, но все складывалось неправильно, я использовал ОСРВ, чтобы переключать 3 светодиода, но он освещает только 2 из них без переключения! случайные 2 светодиода, что бы я ни менял приоритеты или время задержки переключения. случайные 2 светодиода просто включаются и ничего более, я попробовал код на симуляции протея и на реальном оборудовании, и та же проблема существует. Может ли кто-нибудь помочь мне с этим?

M / C: ATMEGA32A

RTOS: 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: каждая задача работает хорошо одна, но не вместе!

1 Ответ

0 голосов
/ 01 мая 2020

Как уже упоминалось в комментариях, основная проблема, по-видимому, заключается в том, что доступ к регистру портов, управляющему светодиодами, не защищен ни

PORTB ^= (1<<0); // in task 1
[...]
PORTB ^= (1<<1); // in task 2
[...]
PORTB ^= (1<<2); // in task 3
  1. atomi c
  2. ( путем отключения прерываний во время доступа или мер RTOS, таких как мьютекс)
  3. , развернутых для одной уникальной задачи:

Возможно, вводит в заблуждение, что доступ к регистру HW выполняется с использованием одиночная инструкция в коде C каждый раз. Тем не менее, это не помогает, потому что компилятор генерирует несколько инструкций ассемблера (например, загрузить предыдущее значение порта для регистрации, изменить это значение регистра, записать его обратно в порт). Таким образом, одна задача может прервать другую между этими инструкциями ассемблера / процессора и изменить промежуточное значение. Несколько задач, записывающих «свои» значения регистра в порт, поочередно могут вернуть другие задачи, которые только что были записаны в порт, поэтому вы пропускаете мигание (или несколько, если это происходит систематически).

Поэтому решение состоит в том, чтобы защитить задания друг от друга. В том же порядке, который пронумерован выше, это может означать одно из следующего:

  1. Проверьте, предлагает ли аппаратное обеспечение регистр "установленного значения" или "значения сброса" рядом с регистром основного порта PORTB. Если это так, запись одного бита в этот порт была бы atomi c способом переключения светодиодов. Извините, что не знаю аппаратного интерфейса Atmega. Возможно, это невозможно, и вам нужно go включить непосредственно в 2. и 3.
  2. a. Отключите прерывания перед изменением регистра порта, затем включите его снова. Таким образом, планировщик задач не будет работать в течение этого периода (= критическая секция), и никто не мешает задаче, которая обращается к оборудованию.

    b. Используйте taskENTER_CRITICAL() / taskEXIT_CRITICAL() c. Используйте мьютекс или подобное.

  3. Создайте четвертое задание, которое ожидает (блокирует) в почтовом ящике / очереди. Всякий раз, когда он получает значение из почтового ящика, он обрабатывает его (например, отправляя XOR в регистр порта). Три существующие задачи не обращаются к регистру светодиодного порта самостоятельно, а вместо этого отправляют такое значение (= сообщение запроса) новой задаче. Присвойте новый приоритет более высокому приоритету, чтобы получить плавный мигающий рисунок.

Если на контроллере возможен вариант 1., он самый быстрый (но для него требуются определенные функции в аппаратная платформа ...). В противном случае я согласен с подсказкой @Richard, вариант 2.b. являются самыми быстрыми (2.a. такими же быстрыми, но не такими чистыми, потому что вы нарушаете многоуровневую структуру FreeRTOS lib).

Вариант 2. c. может привести к значительным накладным расходам, и вариант 3. очень чистый, но полный перебор в вашей ситуации: если ваш вопрос действительно касается только мигающих светодиодов, оставьте бульдозер в гараже и выберите вариант 2.

...