Подтвердить что ты не робот

Как добиться многозадачности в микроконтроллере?

Я написал программу для наручных часов с использованием микроконтроллера 8051 с использованием Embedded (C). В общей сложности имеется 6 7-сегментных дисплеев:

         _______________________
        |      |       |        |   two 7-segments for showing HOURS
        | HR   | MIN   |   SEC  |   two 7-segments for showing MINUTES and
        |______._______.________|   two 7-segments for showing SECONDS
          7-segment LED display

Чтобы обновить часы, минуты и секунды, мы использовали три петли for. Это означает, что сначала будут обновляться секунды, затем минуты, а затем часы. Затем я спросил своего профессора, почему мы не можем обновлять одновременно (я имею в виду увеличение часов через час, не дожидаясь обновления минут). Он сказал, что мы не можем выполнять параллельную обработку из-за последовательного выполнения инструкций.

Вопрос:

Цифровая поздравительная открытка, которая будет воспроизводить музыку непрерывно, одновременно мигая светодиодом. Цифровой будильник генерирует звуковые сигналы в определенное время. Пока он воспроизводит звук, время продолжит обновление. Таким образом, звук и приращение времени обе работают параллельно. Как они достигли этих результатов при последовательном выполнении?

Как запускать несколько задач одновременно (планирование) в микроконтроллере?

4b9b3361

Ответ 1

Во-первых, что с этим последовательным исполнением. Там всего одно ядро, одно программное пространство, один счетчик. MPU выполняет одну команду за раз, а затем переходит в другую последовательно. В этой системе нет неотъемлемого механизма, чтобы заставить его перестать делать одно и начинать делать другое - это все одна программа, и полностью в руках программиста, какая последовательность будет и что она будет делать; он будет длиться непрерывно, по одной команде за раз в последовательности, пока работает MPU, и ничего больше не произойдет, если только программатор не сделал это первым.

Теперь, для многозадачности:

Обычно операционные системы обеспечивают многозадачность, с довольно сложными алгоритмами планирования.

Обычно микроконтроллеры работают без операционной системы.

Итак, как вы достигаете многозадачности в микроконтроллере?

Простым ответом является "вы не делаете". Но, как обычно, простой ответ редко охватывает более 5% случаев...

У вас было бы очень тяжелое время писать реальную, превентивную многозадачность. Большинство микроконтроллеров просто не имеют возможностей для этого, и то, что процессор Intel делает с помощью нескольких конкретных инструкций, потребует от вас ввода мили кода. Лучше забывайте классическую многозадачность для микроконтроллеров, если вам действительно нечего делать с вашим временем.

Теперь есть два обычных подхода, которые часто используются вместо этого, с гораздо меньшими проблемами.

Прерывания

Большинство микроконтроллеров имеют разные источники прерываний, часто включая таймеры. Таким образом, основной цикл выполняет одно задание непрерывно, и когда таймер отсчитывает до нуля, выдается прерывание. Основной цикл останавливается, и выполнение переходит к адресу, известному как "вектор прерывания". Там запускается другая процедура, выполняющая другую одноразовую задачу. Как только это закончится (возможно, при необходимости сбросьте таймер), вы вернетесь из прерывания, и основной цикл будет возобновлен.

Микроконтроллеры часто имеют несколько таймеров, и вы можете назначить одну задачу на каждый таймер, не говоря уже о задачах на других внешних прерываниях (например, нажатие клавиши клавиатуры или нажатие на данные по RS232.)

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

Совместная многозадачность

Вот как это было сделано в первые дни. Вам нужно написать свои "задачи" как подпрограммы, каждая из которых имеет конечный конечный автомат (или один проход цикла) внутри, а "ОС" - это простой цикл переходов к последовательным задачам в последовательности.

После каждого перехода MPU начинает выполнение заданной задачи и будет продолжаться до тех пор, пока задача не вернет управление после первого сохранения своего состояния, чтобы восстановить его, когда оно запустилось снова. Каждый проход задания задания должен быть очень коротким. Любые циклы задержки должны быть заменены состояниями ожидания в двигателе с конечным состоянием (если условие не выполняется, верните. Если это так, измените состояние.) Все длинные петли должны быть развернуты в разные состояния ("Состояние: копирование блока данных, скопировать байт N, увеличить N, N = конец? да: следующее состояние, нет: управление возвратом)

Написание этого пути сложнее, но решение более надежное. В вашем случае у вас может быть четыре задачи:

  • часы
  • отображение обновления
  • воспроизведение звука
  • Мигает светодиодом

Часы возвращают управление, если новая секунда не прибыла. Если это так, оно пересчитывает количество секунд, минут, часов, даты и затем возвращает.

Дисплей обновляет отображаемые значения. Если вы мультиплексируете над цифрами на 8-сегментном дисплее, каждый проход будет обновлять одну цифру, следующий проход - следующий и т.д.

Воспроизведение звука будет ждать (выход), пока он не будет днем ​​рождения. Если это день рождения, выберите значение выборки из памяти, выведите его на динамик, урожай. Опционально, если вы были вызваны раньше, чем вы должны были выпустить следующий звук.

Мигает - ну, выведите правильное состояние на светодиод, урожай.

Очень короткие петли - скажем, 10 итераций из 5 строк - все еще разрешены, но что-то более длинное должно быть преобразовано в состояние механизма конечного состояния, которое имеет этот процесс.

Теперь, если вы чувствуете хардкор, вы можете попробовать...

упреждающая многозадачность.

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

Задача вашей ОС запускается из прерывания таймера.

После начала прерывания задача ОС должна сохранять все текущее состояние волатильности последней задачи - регистры, адрес возврата прерывания (из которого задача должна быть возобновлена), текущий указатель стека, сохраняя это в записи эта задача.

Затем, используя алгоритм планировщика, он выбирает другой процесс из списка, который должен начаться прямо сейчас; восстанавливает все его состояние, а затем перезаписывает собственный адрес возврата от прерывания по адресу, где этот процесс был остановлен, когда предварительно выгружен. По окончании прерывания нормальная работа вытесненного процесса возобновляется до следующего прерывания, которое снова переключает управление на ОС.

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

Ответ 2

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

Иллюзия одновременного выполнения. Технически ваш профессор прав и обновление одновременно невозможно. Однако процессоры очень быстрые. Для многих задач они могут выполняться последовательно, например, обновлять каждый 7-сегментный дисплей по одному, но он делает это так быстро, что человеческое восприятие не может сказать, что каждый дисплей обновлялся последовательно. То же самое касается звука. Большинство слышимых звуков находятся в диапазоне килогерца, а процессоры работают в мегагерцевом диапазоне. Процессор имеет много времени, чтобы играть часть звука, делать что-то еще, а затем возвращаться к воспроизведению звука без вашего уха, способного обнаружить разницу.

Прерывания. SF отлично справлялся с выполнением прерываний, поэтому я буду замаскировать механику и больше поговорить об аппаратном обеспечении. Большинство микроконтроллеров имеют небольшие аппаратные модули, которые работают одновременно с выполнением команд. Таймеры, UARTS и SPI являются общими модулями, которые выполняют определенное действие, в то время как основная часть процессора выполняет инструкции. Когда данный модуль завершает свою задачу, он уведомляет процессор, и процессор переходит к коду прерывания для модуля. Этот механизм позволяет выполнять такие действия, как передача байта над uart (что относительно медленно) при выполнении инструкций.

PWM - PWM (широтно-импульсная модуляция) - это аппаратный модуль, который по существу генерирует квадратную волну, по два за раз, но квадраты не должны быть четными (здесь я упрощаю), Один может быть длиннее другого, или они могут быть одного размера. Вы настраиваете на аппаратном уровне размер квадратов, а затем PWM генерирует их непрерывно. Этот модуль может использоваться для привода двигателей или даже генерации звука, где скорость двигателя или частота звука зависят от соотношения двух квадратов. Чтобы воспроизводить музыку, процессор должен был бы только изменить соотношение, когда настало время для изменения примечания (возможно, на основе прерывания по таймеру), и тем временем он может выполнять другие инструкции.

DMA - DMA (прямой доступ к памяти) - это особый тип аппаратного обеспечения, который автоматически копирует байты из одного места памяти в другое. Что-то вроде АЦП может непрерывно записывать преобразованное значение в определенный регистр в памяти. Контроллер DMA может быть сконфигурирован для непрерывного чтения с одного адреса (выход ADC) при записи последовательно в диапазон памяти (например, буфер для приема нескольких преобразований АЦП до усреднения). Все это происходит в аппаратном обеспечении, в то время как основной процессор выполняет инструкции.

Таймеры, UART, SPI, ADC и т.д.. Существует много других аппаратных модулей (слишком много для покрытия здесь), которые выполняют определенную задачу одновременно с выполнением программы.

TL/DR. Хотя инструкции программы могут выполняться только последовательно, процессор обычно может выполнять их достаточно быстро, чтобы они, как представляется, выполнялись одновременно. Между тем, у большинства микроконтроллеров есть дополнительное оборудование, которое одновременно выполняет определенные задачи с выполнением программы.

Ответ 3

Ответ Зак и SF. красиво покрывает общую картину. Но иногда рабочий пример ценен.

Хотя я мог бы с готовностью предложить просматривать исходный комплект ядра Linux (который является открытым исходным кодом и обеспечивает многозадачность даже на одноядерных машинах), это не лучшее место для понимания того, как реально реализовать планировщик.

Лучше всего начать с исходного набора к одной из сотен (если не тысяч) операционных систем реального времени. Многие из них с открытым исходным кодом, и большинство из них могут работать даже на чрезвычайно маленьких процессорах, включая 8051. Я опишу Micrium uC/OS-II здесь более подробно, потому что у него есть типичный набор функций, и это тот, широко используется. Другие, которые я оценил в прошлом, включают OS-9, eCos и FreeRTOS. С этими именами в качестве отправной точки наряду с такими ключевыми словами, как "RTOS", Google вознаградит вас за имена многих других.

Мой первый доступ к ядру RTOS был бы uC/OS-II (или его новый семейный член uC/OS-III). Это коммерческий продукт, который начал свою жизнь как учебное упражнение для читателей журнала Embedded Systems Design. Журнальные статьи и их прилагаемый исходный код стали предметом одной из лучших книг по этому вопросу. ОС является открытым исходным кодом, но она не имеет лицензионных ограничений для коммерческого использования. В интересах раскрытия информации я являюсь автором порта uC/OS-II для ColdFire MCF5307.

Поскольку он был первоначально написан как учебный инструмент, исходный код хорошо документирован. Текст книги (как и на втором издании на моем шельфе, где-то где-то, по крайней мере) хорошо написан и хорошо разбирается в каждой из функций, которые он поддерживает.

Я успешно использовал его в нескольких проектах разработки продукта и буду рассматривать его снова для проекта, который требует многозадачности, но не должен переносить вес полной ОС, такой как Linux.

uC/OS-II обеспечивает упреждающий планировщик задач, а также полезный набор примитивов связи между задачами (семафор, мьютекс, почтовый ящик, очередь сообщений), таймеры и потоковый распределитель памяти.

Он также поддерживает приоритет задачи и включает предотвращение блокировки при правильном использовании.

Он полностью написан в подмножестве стандарта C (отвечающем почти всем требованиям MISRA-C: 1998), которые помогли ему он должен получать различные сертификаты безопасности.

Хотя мои приложения никогда не были в критически важных системах безопасности, было приятно узнать, что ядро ​​ОС, на котором я стоял, достигло этих рейтингов. Это обеспечило уверенность в том, что наиболее вероятной причиной, по которой у меня была ошибка, было либо неправильное понимание того, как примитив работал, либо, возможно, более вероятно, был ошибкой в ​​моей логике приложений.

Большинство RTOS (и, особенно, uC/OS-II) могут работать в ограниченных ресурсах. uC/OS-II может быть построена всего лишь на 6 Кбайт кода и всего лишь 1 Кбайт оперативной памяти, требуемой для структур ОС.

Суть в том, что очевидный concurrency может быть достигнут различными способами, и одним из таких способов является использование ядра ОС, предназначенного для планирования и выполнения каждой параллельной задачи параллельно путем совместного использования ресурсов последовательного CPU среди всех задач. Для простых случаев вам может понадобиться только обработчики прерываний и основной цикл, но когда ваши требования растут до уровня реализации нескольких протоколов, управления отображением, управлением пользовательскими вводами, вычислением фона и мониторингом общего состояния системы, стоя на колодце -описанное ядро ​​RTOS вместе с известными рабочими примитивами связи может сэкономить много усилий по разработке и отладке.

Ответ 4

Хорошо, я вижу много земли, на которую ссылаются другие ответы; так что, надеюсь, я не превращу это в нечто большее, чем я предполагаю. (TL; DR: Девушка на помощь!: D). Но у меня есть (что я считаю) очень хорошим решением; поэтому я надеюсь, что вы сможете это использовать. У меня только небольшой опыт работы с 8051 [& star;]; хотя я работал в течение ~ 3 месяцев (плюс ~ еще 3 раза) на другом микроконтроллере с умеренным успехом. В результате я получил немного почти все, что может предложить маленькая вещь: последовательная связь, сигналы SPI, PWM, сервоуправление, DIO, термопары и т.д. В то время как я работал над этим, мне повезло и наткнулось на отличное (IMO) решение для (кооперативного) "потокового" планирования, которое хорошо сочеталось с небольшим количеством дополнительных материалов в реальном времени, выполненных с прерываниями в ПОС. И, конечно же, другие обработчики прерываний для других устройств.

pt_thread: Придумал Адам Дункельс (с Оливером Шмидтом) (v1.0 выпущен в феврале., 2005), его сайт - отличное введение в них, палочка включает загрузки через v1.4 с октября 2006 года; и я очень рад, что я снова посмотрел, потому что нашел; но есть статья от января 2009 года, в которой говорится, что Ларри Руан использовал методы, основанные на событиях "[для] полной переориентации [с использованием GCC и с] очень приятным синтаксисом", и доступным на SourceForge. К сожалению, похоже, что с 2009 года обновлений нет. но версия 2006 года мне очень понравилась. В последнем номере новостей (с декабря 2009 года) отмечается, что "Sonic Unleashed" указал в своем руководстве, что использовались прототипы!

Одна из вещей, которые, как мне кажется, удивительны в pt_threads, - это то, что они такие простые; и, независимо от преимуществ новой версии (Ruane), она, безусловно, более сложна. Хотя, возможно, стоит взглянуть на это, я собираюсь придерживаться оригинальной реализации Dunkels здесь. Его оригинальная библиотека pt_threads "состоит из: пяти файлов заголовков. И, действительно, это похоже на преувеличение, поскольку как только я минимизировал несколько макросов и других вещей, удалил разделы doxygen, примеры и отбросил комментарии до минимального минимума, который я все еще чувствовал, давал объяснение, он работает только вокруг 115 строк (см. Ниже).

Есть примеры, включенные в исходный tarball, и очень хороший .pdf-документ (или .html), доступный на его сайте (связанный выше). Но позвольте мне кратко рассказать о некоторых концепциях. (Не сами макросы, мне потребовалось некоторое время, чтобы понять их, и они не нужны только для использования функциональности.: D)

К сожалению, у меня не хватило времени на вечер; но я попытаюсь вернуться в какой-то момент завтра, чтобы написать небольшой пример; в любом случае на его веб-сайте имеется множество ресурсов, связанных выше; это довольно простая процедура, сложная часть для меня (как я полагаю, это с какой-либо совместной многопотоковой версией: Win 3.1 any?: D) обеспечивал, чтобы я правильно пересчитал код, чтобы не перераспределять время Мне нужно было обработать следующее, прежде чем уступить pt_thread.

Надеюсь, это даст вам начало; дайте мне знать, как это происходит, если вы попробуете это!

FILE: pt.h
    #ifndef __PT_H__
    #define __PT_H__

    #include "lc.h"

    // NOTE: the enums are mine to compress space; originally all were #defines
    enum PT_STATUS_ENUM { PT_WAITING, PT_YIELDED, PT_EXITED, PT_ENDED };

    struct pt { lc_t lc; }                 // protothread control structure (pt_thread)
    #define PT_INIT(pt) LC_INIT((pt)->lc)  // initializes pt_thread prior to use

    // you can use this to declare pt_thread functions
    #define PT_THREAD(name_args) char name_args
    // NOTE: looking at this, I think I might define my own macro as follows, so as not
    //       to have to redclare the struct pt *pt every time.
        //#define PT_DECLARE(name, args) char name(struct pt *pt, args)

    // start/end pt_thread (inside implementation fn); must always be paired
    #define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)->lc)
    #define PT_END(pt) LC_END((pt)->lc);PT_YIELD_FLAG = 0;PT_INIT(pt);return PT_ENDED;}

    // {block, yield} 'pt' {until,while} 'c' is true
    #define PT_WAIT_UNTIL(pt,c) do { \
        LC_SET((pt)->lc); if(!(c)) {return PT_WAITING;} \
    } while(0)

    #define PT_WAIT_WHILE(pt, cond) PT_WAIT_UNTIL((pt), !(cond))

    #define PT_YIELD_UNTIL(pt, cond)            \
      do { PT_YIELD_FLAG = 0; LC_SET((pt)->lc); \
        if((PT_YIELD_FLAG == 0) || !(cond)) { return PT_YIELDED; } } while(0)

    // NOTE: no corresponding "YIELD_WHILE" exists; oversight? [shelleybutterfly]
    //#define PT_YIELD_WHILE(pt,cond) PT_YIELD_UNTIL((pt), !(cond))

    // block pt_thread 'pt', waiting for child 'thread' to complete
    #define PT_WAIT_THREAD(pt, thread) PT_WAIT_WHILE((pt), PT_SCHEDULE(thread))

    // spawn pt_thread 'ch' as child of 'pt', waiting until 'thr' exits
    #define PT_SPAWN(pt,ch,thr) do { \
        PT_INIT((child)); PT_WAIT_THREAD((pt),(thread)); } while(0)

    // block and cause pt_thread to restart its execution at its PT_BEGIN()
    #define PT_RESTART(pt) do { PT_INIT(pt); return PT_WAITING; } while(0)

    // exit the pt_thread; if a child, then parent will unblock and run
    #define PT_EXIT(pt) do { PT_INIT(pt); return PT_EXITED; } while(0)

    // schedule pt_thread: fn ret != 0 if pt is running, or 0 if exited
    #define PT_SCHEDULE(f) ((f) lc); \
            if(PT_YIELD_FLAG == 0) { return PT_YIELDED; } } while(0)


FILE: lc.h
    #ifndef __LC_H__
        #define __LC_H__

    #ifdef LC_INCLUDE
        #include LC_INCLUDE
    #else
        #include "lc-switch.h"
    #endif /* LC_INCLUDE */

    #endif /* __LC_H__ */


FILE: lc-switch.h
    // WARNING: implementation using switch() won't work with an LC_SET() inside a switch()!
    #ifndef __LC_SWITCH_H__
    #define __LC_SWITCH_H__

    typedef unsigned short lc_t;

    #define LC_INIT(s) s = 0;
    #define LC_RESUME(s) switch(s) { case 0:
    #define LC_SET(s) s = __LINE__; case __LINE__:
    #define LC_END(s) }

    #endif /* __LC_SWITCH_H__ */


FILE: lc-addrlabels.h
    #ifndef __LC_ADDRLABELS_H__
    #define __LC_ADDRLABELS_H__

    typedef void * lc_t;

    #define LC_INIT(s) s = NULL
    #define LC_RESUME(s) do { if(s != NULL) { goto *s; } } while(0)
    #define LC_CONCAT2(s1, s2) s1##s2
    #define LC_CONCAT(s1, s2) LC_CONCAT2(s1, s2)
    #define LC_END(s)

    #define LC_SET(s) \
      do {LC_CONCAT(LC_LABEL, __LINE__):(s)=&&LC_CONCAT(LC_LABEL,__LINE__);} while(0)

    #endif /* __LC_ADDRLABELS_H__ */


FILE: pt-sem.h
    #ifndef __PT_SEM_H__
    #define __PT_SEM_H__

    #include "pt.h"

    struct pt_sem { unsigned int count; };

    // macros to initiaize, await, and signal a pt_sem semaphore
    #define PT_SEM_INIT(s, c) (s)->count = c
    #define PT_SEM_WAIT(pt, s) do \
        { PT_WAIT_UNTIL(pt, (s)->count > 0); -(s)->count; } while(0)
    #define PT_SEM_SIGNAL(pt, s) ++(s)->count

    #endif /* __PT_SEM_H__ */


[& star;]   * около недели, изучая микроконтроллеры [†] и неделю, играя с ним во время оценки, чтобы увидеть, он может удовлетворить наши потребности в небольшом удаленном модуле ввода-вывода с возможностью замены. (длинный рассказ, короткий: нет)

[†]   8051 Microcontroller, Third Edition * был предложен мне как 8051 программирование "библия" Я не знаю, было это или нет, но я, конечно же, мог окунуться в него, используя его. [‡]

[‡]   и даже просматривая это снова, я не вижу в этом ничего, что нравится.:) ну, я имею в виду... Хотел бы я, чтобы я не купил две копии; но они были настолько дешевы!

LICENSE AGREEMENT (where applicable)
This post contains code based on (or taken from) 'The Protothreads Library' (referred to herein and henceforth as "PTLIB"; including v1.4 and earlier revisions) relying extensively on the source code as well as the documentation for PTLIB. PTLIB original source code and documentation was received from, and freely available for download at the author PTLIB site 'http://dunkels.com/adam/pt/', available through a link on the downloads page at 'http://dunkels.com/adam/pt/download.html' or directly via 'http://dunkels.com/adam/download/pt-1.4.tar.gz'. This post consists of original text, for which I hereby give to you (with love!) under a full waiver of whatever copyright interest I may have, under the following terms: "copyheart ♥ 2014, shelleybutterfly, share with love!"; or, if you prefer, a fully non-restrictive, attribution-only license appropriate to the material (such as Apache 2.0 for software; or CC-BY license for text) so that you may use it as you see fit, so that it may best suit your needs. This post also contains source code, almost entirely created from the original source by removing explanatory material, reformatting, and paraphrasing the in-line documentation/comments, as well as a few modifications/additions by me (shelleybutterfly on the stackexchange network). Anything derivative of PTLIB for which I may have, legally, gained any copyright or other interest, I hereby cede all such interest back to and all copyright interest in the original work to the original copyright holder, as specified in the license from PTLIB, which follows this agreement. In any jurisdiction where it is not possible for the terms above to apply to you for whatever reason, then, for whatever interest I have in the material, I hereby offer it to you under any non-restrictive, attribution-only, license of your choosing; or, should this also not be possible, then I give permission to stack exchange inc to provide it to you under whatever terms the y determine to be acceptable in your jurisdiction. All source code from PTLIB, and that which is derivative of PTLIB, that is not covered under other terms detailed above hereby provided to 'stack exchange inc' and to you under the following agreement:


LICENSE AGREEMENT for "The Protothreads Library"
Copyright (c) 2004-2005, Swedish Institute of Computer Science. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following    disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following    disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the Institute nor the names of its contributors may be used to endorse or promote products derived    from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS `AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Author: Adam Dunkels

Ответ 5

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

То, как один процессор может, кажется, делать несколько вещей одновременно, - это быстрое переключение между задачами, а также использование таймеров, прерываний и независимых аппаратных единиц, которые могут делать что-то независимо от процессора. (см. ответ @Zack для приятного обсуждения и стартового списка HW) Итак, для вашей поздравительной открытки CPU может сказать немного аудио-аппаратного обеспечения "воспроизвести этот кусок звука", затем мигать светодиодом, затем вернуться и загрузить следующий бит звука до того, как первая часть закончит игру. В этой ситуации процессор может занять 1 мс времени для загрузки звука, который может воспроизводиться в течение 5 мс реального времени, оставляя вам 4 мс времени, чтобы сделать что-то еще, прежде чем загружать следующий бит звука.

Цифровые часы могут подавать звуковой сигнал, устанавливая бит аппаратного обеспечения ШИМ для вывода на некоторой частоте пьезо-зуммера, таймера прерывания, чтобы остановить звуковой сигнал, затем выключите и проверьте счетчик в реальном времени, чтобы узнать, светодиодные индикаторы должны быть обновлены. Когда таймер запускает прерывание, ваш код отключает PWM.

Детали будут различаться в зависимости от аппаратного обеспечения чипа, а перебор данных - способ узнать, какие возможности может иметь данный микроконтроллер, и как получить к нему доступ.

Ответ 6

У меня были хорошие впечатления от Freertos, хотя он использует справедливую бит памяти. Freertos дает вам истинную превентивную резьбу, есть тонны портов, если вы когда-либо захотите обновить эти пыльные старые 8051-ые, там семафоры и очереди сообщений, приоритеты и всевозможные вещи, и это абсолютно бесплатно. Я работал с портом arduino лично, но он, кажется, один из самых популярных бесплатных rtosses.

Я думаю, что они продают книгу, которая не является бесплатной, но достаточно информации на их веб-сайте и в примерах arduino, чтобы в значительной степени понять это.