Запуск FreeRTOS на MIK32: различия между версиями
(не показано 35 промежуточных версий этого же участника) | |||
Строка 9: | Строка 9: | ||
==== Загрузка файлов проекта ==== | ==== Загрузка файлов проекта ==== | ||
Есть два пути, по которым можно двигаться, один из них скорее более длительный и трудоемкий - взять за основу один из официальных портов RISC_V с сайта freertos.org и модернизировать его. | |||
[[Файл:Freertos 1.png|центр|мини]] | [[Файл:Freertos 1.png|центр|мини]] | ||
Или же взять за основу порт от компании syntacore, который будет по максимуму приближен к нашему микроконтроллеру, так как он основан на ядре SCR1 разработки этой компании. И этим путем мы и пойдем. Исходный порт находится в SDK по ссылке https://syntacore.com/page/products/sw-tools , имя файла 202212-sp1-sc-dt-win.zip. | |||
[[Файл:Freertos 1a.png|центр|мини]]И мы пойдем именно этим путем! | |||
==== Краткое пояснение - что-где ==== | ==== Краткое пояснение - что-где ==== | ||
В первую очередь, | В первую очередь найдем место, где находятся файлы самого движка ОСРВ, после чего нам нужно будет добавить эти файлы в свой проект-шаблон. В архиве SDK искомые файлы находятся здесь: | ||
[[Файл:Freertos | [[Файл:Freertos 2a.png|центр|мини]] | ||
В подпапке include - все необходимые хидеры. | В подпапке include - все необходимые хидеры. | ||
Строка 33: | Строка 34: | ||
[[Файл:Freertos 10.png|центр|мини]] | [[Файл:Freertos 10.png|центр|мини]] | ||
остается нажать кнопку Finish. После этого проект должен вполне успешно собираться (Ctrl+B) и работать в микроконтроллере . Можно проверить это при желании. | остается нажать кнопку Finish. После этого проект должен вполне успешно собираться (Ctrl+B) и работать в микроконтроллере . Можно проверить это при желании. | ||
=== Модификация проекта === | |||
==== Копирование файлов ОСРВ в проект ==== | ==== Копирование файлов ОСРВ в проект ==== | ||
На данном существует вариативность действий, но предлагается оставить структуру наиболее близкой к той, которая наблюдается в репозитории freertos. Создадим папку FreeRTOS в директории src. Сначала копируем папку с хидерами и Си-файлы движка опtрационной системы в созданную src\FreeRTOS. | На данном этапе существует вариативность действий, но предлагается оставить структуру наиболее близкой к той, которая наблюдается в репозитории syntacore/freertos. Создадим папку FreeRTOS в директории src. Сначала копируем папку с хидерами и Си-файлы движка опtрационной системы в созданную src\FreeRTOS. | ||
[[Файл:Freertos 11.png|центр|мини]] | [[Файл:Freertos 11.png|центр|мини]] | ||
В этом месте создадим папку portable и скопируем в нее только необходимое, это файлы репозитория которые находятся в \portable\GCC\RISC-V\, далее нужно скопировать папку portable\Common (работа с MPU при наличии такового), а так же MemMang (модели памяти). На данном этапе проект должен выглядеть следующим образом: | В этом месте создадим папку portable и скопируем в нее только необходимое, это файлы репозитория которые находятся в \portable\GCC\RISC-V\, далее нужно скопировать папку portable\Common (работа с MPU при наличии такового), а так же MemMang (модели памяти). На данном этапе проект должен выглядеть следующим образом: | ||
[[Файл:Freertos 12.png|центр|мини]] | [[Файл:Freertos 12.png|центр|мини]] | ||
Далее, так как мы должны выбрать только одну модель памяти из 5 вариантов, то удалим все файлы кроме heap_1.c - это самая базовая опция, без возможности очистки и удаления, более подробно про модели памяти можно узнать на сайте freertos.org. | |||
[[Файл:Freertos 13.png|центр|мини]] | [[Файл:Freertos 13.png|центр|мини]] | ||
На этом структура проекта в части исходных кодов FreerRTOS сформирована. | На этом структура проекта в части основных исходных кодов FreerRTOS сформирована. | ||
==== Файл ctr0.s ==== | |||
Следует найти этот системный файл в проекте и убедиться, что он не является файлом из внешнего репозитория (по умолчанию так). Поэтому следует этот важный файл содержать локально. Иначе все правки, которые мы внесем в этот файл отразятся на остальных проектах, которые этот файл используют. | |||
Как было в случае копирования шаблона: | |||
[[Файл:Freertos 14.png|центр|мини]] | |||
Поменял путь к папке, чтобы она адресовала именно в директорию с проектом: | |||
[[Файл:Freertos 15.png|центр|мини]]После чего открываем этот файл и редактируем его. Около 90 строки находим код:<syntaxhighlight lang="c"> | |||
.org 0xC0 | |||
trap_entry: | |||
j raw_trap_handler | |||
</syntaxhighlight>И меняем его на <syntaxhighlight lang="c"> | |||
//.org 0xC0 | |||
trap_entry: | |||
//j raw_trap_handler | |||
j freertos_risc_v_trap_handler | |||
</syntaxhighlight> | |||
==== Конфигурирование порта ==== | |||
Далее, нужно добавить несколько пользовательских конфигурационных файлов в корневую директорию \src. Для этого нужно скопировать туда FreeRTOSConfig.h и scr_sys.c, найдя их в репозитории \202212-sp1-sc-dt-win.zip/\sc-dt_2022.12-sp1\workspace\freertos\Demo\RISC-V_Syntacore_SCRx_GCC\. Первый отвечает за конфигурацию опций операционной системы, второй - за её связь с аппаратной частью ядра процессора (настройка системного таймера ядра и его периодическое прерывание). | |||
В файле FreeRTOSConfig.h следует отключить часть модулей, чтобы объем прошивки позволил поместиться в 8кБ. | |||
* Отключаем программные таймеры | |||
<syntaxhighlight lang="c"> | |||
#define configUSE_TIMERS 0 | |||
.... | |||
#define INCLUDE_xTimerPendFunctionCall 0 | |||
</syntaxhighlight> | |||
* Комментируем подключение файла arch.h | |||
<syntaxhighlight lang="c"> | |||
//#include "arch.h" | |||
</syntaxhighlight> | |||
* Меняем системную частоту на 32МГц | |||
<syntaxhighlight lang="c"> | |||
#define configCPU_CLOCK_HZ ( 32000000 ) | |||
</syntaxhighlight> | |||
* Устанавливаем размер памяти для ОСРВ в 3 кБ. Этого не много, но для демонстрации более чем достаточно. | |||
<syntaxhighlight lang="c"> | |||
#define configTOTAL_HEAP_SIZE ( ( size_t ) 3*1024 ) | |||
</syntaxhighlight>На этом редактирование этого файла закончена. Следующий файл на редактирование - scr_sys.c. | |||
Комментируем эту строку, так как у нас иначе прописан системный таймер:<syntaxhighlight lang="c"> | |||
//#include "drivers/rtc.h" | |||
</syntaxhighlight>И добавляем эти хидеры:<syntaxhighlight lang="c"> | |||
#include <scr1_csr_encoding.h> | |||
#include "riscv_csr_encoding.h" | |||
#include "scr1_timer.h" | |||
#include <mcu32_memory_map.h> | |||
</syntaxhighlight>Удаляем или комментируем эти строки:<syntaxhighlight lang="c"> | |||
//static const unsigned long rtc_ticks_per_timer_shot = PLF_HZ / configTICK_RATE_HZ; | |||
//static sys_tick_t next_rtc_timer_shot = 0; | |||
</syntaxhighlight>Далее следует изменить инициализацию системного таймера на ядре и его обработчик на следующие (старое закомментировано):<syntaxhighlight lang="c"> | |||
void vPortSetupTimerInterrupt( void ) | |||
{ | |||
//#if 0 | |||
// rtc_setcmp_offset(rtc_us2ticks(1000000 / configTICK_RATE_HZ)); | |||
//#else | |||
// sys_tick_t t = rtc_now() + rtc_ticks_per_timer_shot; | |||
// rtc_setcmp(t); | |||
// next_rtc_timer_shot = t + rtc_ticks_per_timer_shot; | |||
//#endif | |||
// /* enable timer interrupts */ | |||
// rtc_interrupt_enable(); | |||
SCR1_TIMER->TIMER_DIV = 320-1;//32000-1;//1kHz | |||
*(unsigned long long *) &SCR1_TIMER->MTIMECMP = 10; | |||
*(unsigned long long *) &SCR1_TIMER->MTIME = 0; | |||
SCR1_TIMER->TIMER_CTRL |= SCR1_TIMER_ENABLE_M; | |||
set_csr(mie, MIE_MTIE); | |||
} | |||
</syntaxhighlight>Здесь загружаются инициализационные значения в таймер, и настраивается делитель таким образом, чтобы у нас происходило срабатывание 1000 раз в секунду. | |||
И обработчик:<syntaxhighlight lang="c"> | |||
void ext_trap_handler(void); | |||
void scr_systick_handler(void) | |||
{ | |||
//#if 0 | |||
// rtc_setcmp_offset(rtc_us2ticks(1000000 / configTICK_RATE_HZ)); | |||
//#else | |||
// sys_tick_t t = next_rtc_timer_shot; | |||
// rtc_setcmp(t); | |||
// next_rtc_timer_shot = t + rtc_ticks_per_timer_shot; | |||
//#endif | |||
unsigned long mcause = read_csr(mcause); | |||
if ( (mcause & 0xF) == 7 && (mcause & (1<<31)) ) | |||
{ | |||
*(unsigned long long *) &SCR1_TIMER->MTIMECMP = *(unsigned long long *) &SCR1_TIMER->MTIME + 100; | |||
if (xTaskIncrementTick() != pdFALSE) | |||
vTaskSwitchContext(); | |||
} | |||
else | |||
{ | |||
ext_trap_handler(); | |||
} | |||
} | |||
</syntaxhighlight>Здесь мы перезагружаем регистры сравнения таймера в случае если сработал таймер, а если сработало иное пользовательское прерывание, то мы отправляемся на его обработку в ext_trap_handler. В исходном проекте Синтакора отсутствовал переход на пользовательские прерывания. | |||
Так же ради экономии места следует избавиться от всех использований printf в этом файле (закомментировать их). | |||
==== Настройки среды ==== | |||
Для успешной работы компилятора нужно прописать пути. Открываем свойства проекта и заходим в опции компилятора. И вносим директории, чтобы выглядело так: | |||
[[Файл:Freertos 16.png|центр|мини]] | |||
Так же настраиваем пути для ассемблера: | |||
[[Файл:Freertos 18.png|центр|мини]] | |||
И макрос для определения функции обработчика прерываний системного таймера (оно же общее прерывание) | |||
[[Файл:Freertos 17.png|центр|мини]]После этих правок проект должен собираться без ошибок, но это еще не всё. | |||
==== Настройка линкера ==== | |||
По умолчанию в проекте есть подключенный скрипт линкера. Но он опять же адресуется в папку shared. | |||
[[Файл:Freertos 19.png|центр|мини]] | |||
Поэтому рекомендуется создать папку в своем проекте, скопировать туда нужный скрипт (мы будем использовать скрипт для загрузки прошивки в ОЗУ) | |||
[[Файл:Freertos 20.png|центр|мини]]Далее добавляем библиотеки '''c''' и '''gcc''' в опции линкера для корректной работы таких функций как memcpy и ряда системных утилит: | |||
[[Файл:Freertos 21.png|центр|мини]] | |||
==== Оптимизация на размер ==== | |||
Так как памяти мы выделили на прошивку всего 8кБ, то следует активировать оптимизацию кода по размеру, выставив такие опции: | |||
[[Файл:Freertos 22.png|центр|мини]] | |||
==== Добавление потоков и запуск ОСРВ ==== | |||
Это последний шаг. Для демонстрации минимальных возможностей в этом примере создается два потока, в одном из которых не происходит ничего - находится в бездействии. Вторая задача ожидает освобождения семафора. В прерывании от линии GPIO (от кнопки) происходит активация семафора. В следующий момент после его активации во вторая задача активируется и на 50мс засвечивает светодиод.<syntaxhighlight lang="c"> | |||
#include <mcu32_memory_map.h> | |||
#include <pad_config.h> | |||
#include <gpio.h> | |||
#include <power_manager.h> | |||
#include <gpio_irq.h> | |||
#include <epic.h> | |||
#include <scr1_csr_encoding.h> | |||
#include "riscv_csr_encoding.h" | |||
#include "FreeRTOS.h" | |||
#include "task.h" | |||
#include "semphr.h" | |||
SemaphoreHandle_t xSemaphore; | |||
void ext_trap_handler(void) | |||
{ | |||
GPIO_IRQ->CLEAR = 0xFFFF; | |||
EPIC->CLEAR = 0xFFFF; | |||
xSemaphoreGiveFromISR(xSemaphore, NULL); | |||
} | |||
static void test_task1(void *param) | |||
{ | |||
while (1) { | |||
vTaskDelay(500); | |||
} | |||
} | |||
static void test_task2(void *param) | |||
{ | |||
while(1) | |||
{ | |||
if ( xSemaphore && xSemaphoreTake(xSemaphore, 100) == pdTRUE ) | |||
{ | |||
GPIO_2->OUTPUT |= (0b1)<<(7); | |||
vTaskDelay(50); | |||
GPIO_2->OUTPUT &= ~((0b1)<<(7)); | |||
} | |||
} | |||
} | |||
void main() { | |||
PM->CLK_APB_P_SET = PM_CLOCK_GPIO_0_M | |||
| PM_CLOCK_GPIO_1_M | |||
| PM_CLOCK_GPIO_2_M | |||
| PM_CLOCK_GPIO_IRQ_M; | |||
PM->CLK_APB_M_SET = PM_CLOCK_PAD_CONFIG_M | |||
| PM_CLOCK_WU_M | |||
| PM_CLOCK_PM_M | |||
| PM_CLOCK_EPIC_M; | |||
for (volatile int i = 0; i < 10; i++) | |||
; | |||
// LED init | |||
PAD_CONFIG->PORT_2_CFG |= (0b01)<<(7*2); | |||
GPIO_2->DIRECTION_OUT = 1<<(7); | |||
// led on shortly | |||
GPIO_2->OUTPUT |= (0b1)<<(7); | |||
for (long int i = 0; i < 3*100000l; i++) | |||
__asm volatile( "ADDI x0, x1, 0"); // это инструкция-замена NOP | |||
GPIO_2->OUTPUT &= ~((0b1)<<(7)); | |||
for (long int i = 0; i < 3*100000l; i++) | |||
__asm volatile( "ADDI x0, x1, 0"); // это инструкция-замена NOP | |||
write_csr(mtvec, 0x02000000); // конфигурация адреса вектора прерывания | |||
// extint on user key setup begin | |||
PAD_CONFIG->PORT_2_CFG |= (0b01) << (6*2); | |||
GPIO_2->DIRECTION_IN = 1<<(6); | |||
// GPIO_IRQ->LEVEL_SET = 1; | |||
GPIO_IRQ->EDGE = 1 << 2; | |||
GPIO_IRQ->LEVEL_SET = 1 << 2; | |||
// GPIO_IRQ->ANYEDGE_SET = 1; | |||
GPIO_IRQ->CFG = 9 << (2*4); | |||
GPIO_IRQ->ENABLE_SET = 1 << 2; | |||
EPIC->MASK_LEVEL_SET = 1 << EPIC_GPIO_IRQ_INDEX; | |||
// extint on user key setup | |||
/* Create work threads */ | |||
xTaskCreate(test_task1, | |||
"Task1", | |||
128, | |||
( void * ) 1, | |||
tskIDLE_PRIORITY + 1 , | |||
NULL ); | |||
xTaskCreate(test_task2, | |||
"Task2", | |||
128, | |||
( void * ) 1, | |||
tskIDLE_PRIORITY + 1 , | |||
NULL ); | |||
xSemaphore = xSemaphoreCreateBinary(); | |||
vTaskStartScheduler(); | |||
} | |||
</syntaxhighlight>Так же рекомендуется всегда явно прописывать адрес вектора прерывания.<syntaxhighlight lang="c"> | |||
write_csr(mtvec, 0x02000000); | |||
</syntaxhighlight> | |||
=== Выводы === | |||
Образ примера получился размером 5.5кБ и работает он из ОЗУ (для памяти программ выделено 8кБ, для памяти данных тоже 8кб). И его можно с тем же успехом запустить из встроенной энергонезависимой памяти, для этого следует откорректировать расположение памяти программ в файле .ld. И само собой, основная Модель памяти для ОСРВ будет вариант с внешней QSPI флэш. | |||
Некоторые неудобства причиняет отсутствие встроенного векторного контроллера прерываний, но это так же и неудобство и для приложений без применения операционных систем. |
Текущая версия от 08:40, 21 августа 2023
Введение
Большинство современных приложений на микроконтроллерах с 32-битной архитектурой требуют применения операционных систем реального времени (ОСРВ). Таких систем существует довольно много - ombOC, microC/OC-II, ThreadX, chibiOS, openRTOS и многие другие, но, наиболее получивших распространение стала система - FreeRTOS.
FreeRTOS - это бесплатное ответвление проекта openRTOS. Дополнительную прелесть этой системе придает легкая интеграция TCP/IP стека lwIP, так же проект с открытым исходным кодом.
В данной статье будет рассмотрено портирование этого проекта под платформу MIK32. Здесь не будет обучения работе с этой ОСРВ, однако таких материалов в интернете довольно много, поэтому здесь будет информация только про то, что нужно изменить в порте под RISC-V из стандартной поставки с сайта freertos.org.
Первые шаги
Загрузка файлов проекта
Есть два пути, по которым можно двигаться, один из них скорее более длительный и трудоемкий - взять за основу один из официальных портов RISC_V с сайта freertos.org и модернизировать его.
Или же взять за основу порт от компании syntacore, который будет по максимуму приближен к нашему микроконтроллеру, так как он основан на ядре SCR1 разработки этой компании. И этим путем мы и пойдем. Исходный порт находится в SDK по ссылке https://syntacore.com/page/products/sw-tools , имя файла 202212-sp1-sc-dt-win.zip.
И мы пойдем именно этим путем!
Краткое пояснение - что-где
В первую очередь найдем место, где находятся файлы самого движка ОСРВ, после чего нам нужно будет добавить эти файлы в свой проект-шаблон. В архиве SDK искомые файлы находятся здесь:
В подпапке include - все необходимые хидеры.
Папка portable находятся файлы, которые портируют систему на разные архитектуры. Здесь нас будет интересовать именно подпапка \GCC\RISC-V
Рабочая среда Eclipse
Будем работать в среде Eclipse. Скачать уже сконфигурированную оболочку можно в разделе Быстрый старт в Eclipse IDE. После установки среды в рабочей папке можно найти проект-шаблон:
Копируем содержимое этой директории во вновь созданную папку для нашего будущего проекта!
Далее, уже в Eclipse мы втягиваем проект в рабочий worspace, в меню выбираем "File->Import"
и выбираем место, где мы ранее сохранили шаблон-проект
остается нажать кнопку Finish. После этого проект должен вполне успешно собираться (Ctrl+B) и работать в микроконтроллере . Можно проверить это при желании.
Модификация проекта
Копирование файлов ОСРВ в проект
На данном этапе существует вариативность действий, но предлагается оставить структуру наиболее близкой к той, которая наблюдается в репозитории syntacore/freertos. Создадим папку FreeRTOS в директории src. Сначала копируем папку с хидерами и Си-файлы движка опtрационной системы в созданную src\FreeRTOS.
В этом месте создадим папку portable и скопируем в нее только необходимое, это файлы репозитория которые находятся в \portable\GCC\RISC-V\, далее нужно скопировать папку portable\Common (работа с MPU при наличии такового), а так же MemMang (модели памяти). На данном этапе проект должен выглядеть следующим образом:
Далее, так как мы должны выбрать только одну модель памяти из 5 вариантов, то удалим все файлы кроме heap_1.c - это самая базовая опция, без возможности очистки и удаления, более подробно про модели памяти можно узнать на сайте freertos.org.
На этом структура проекта в части основных исходных кодов FreerRTOS сформирована.
Файл ctr0.s
Следует найти этот системный файл в проекте и убедиться, что он не является файлом из внешнего репозитория (по умолчанию так). Поэтому следует этот важный файл содержать локально. Иначе все правки, которые мы внесем в этот файл отразятся на остальных проектах, которые этот файл используют.
Как было в случае копирования шаблона:
Поменял путь к папке, чтобы она адресовала именно в директорию с проектом:
После чего открываем этот файл и редактируем его. Около 90 строки находим код:
.org 0xC0 trap_entry: j raw_trap_handler
И меняем его на
//.org 0xC0 trap_entry: //j raw_trap_handler j freertos_risc_v_trap_handler
Конфигурирование порта
Далее, нужно добавить несколько пользовательских конфигурационных файлов в корневую директорию \src. Для этого нужно скопировать туда FreeRTOSConfig.h и scr_sys.c, найдя их в репозитории \202212-sp1-sc-dt-win.zip/\sc-dt_2022.12-sp1\workspace\freertos\Demo\RISC-V_Syntacore_SCRx_GCC\. Первый отвечает за конфигурацию опций операционной системы, второй - за её связь с аппаратной частью ядра процессора (настройка системного таймера ядра и его периодическое прерывание).
В файле FreeRTOSConfig.h следует отключить часть модулей, чтобы объем прошивки позволил поместиться в 8кБ.
- Отключаем программные таймеры
#define configUSE_TIMERS 0 .... #define INCLUDE_xTimerPendFunctionCall 0
- Комментируем подключение файла arch.h
//#include "arch.h"
- Меняем системную частоту на 32МГц
#define configCPU_CLOCK_HZ ( 32000000 )
- Устанавливаем размер памяти для ОСРВ в 3 кБ. Этого не много, но для демонстрации более чем достаточно.
#define configTOTAL_HEAP_SIZE ( ( size_t ) 3*1024 )
На этом редактирование этого файла закончена. Следующий файл на редактирование - scr_sys.c. Комментируем эту строку, так как у нас иначе прописан системный таймер:
//#include "drivers/rtc.h"
И добавляем эти хидеры:
#include <scr1_csr_encoding.h> #include "riscv_csr_encoding.h" #include "scr1_timer.h" #include <mcu32_memory_map.h>
Удаляем или комментируем эти строки:
//static const unsigned long rtc_ticks_per_timer_shot = PLF_HZ / configTICK_RATE_HZ; //static sys_tick_t next_rtc_timer_shot = 0;
Далее следует изменить инициализацию системного таймера на ядре и его обработчик на следующие (старое закомментировано):
void vPortSetupTimerInterrupt( void ) { //#if 0 // rtc_setcmp_offset(rtc_us2ticks(1000000 / configTICK_RATE_HZ)); //#else // sys_tick_t t = rtc_now() + rtc_ticks_per_timer_shot; // rtc_setcmp(t); // next_rtc_timer_shot = t + rtc_ticks_per_timer_shot; //#endif // /* enable timer interrupts */ // rtc_interrupt_enable(); SCR1_TIMER->TIMER_DIV = 320-1;//32000-1;//1kHz *(unsigned long long *) &SCR1_TIMER->MTIMECMP = 10; *(unsigned long long *) &SCR1_TIMER->MTIME = 0; SCR1_TIMER->TIMER_CTRL |= SCR1_TIMER_ENABLE_M; set_csr(mie, MIE_MTIE); }
Здесь загружаются инициализационные значения в таймер, и настраивается делитель таким образом, чтобы у нас происходило срабатывание 1000 раз в секунду. И обработчик:
void ext_trap_handler(void); void scr_systick_handler(void) { //#if 0 // rtc_setcmp_offset(rtc_us2ticks(1000000 / configTICK_RATE_HZ)); //#else // sys_tick_t t = next_rtc_timer_shot; // rtc_setcmp(t); // next_rtc_timer_shot = t + rtc_ticks_per_timer_shot; //#endif unsigned long mcause = read_csr(mcause); if ( (mcause & 0xF) == 7 && (mcause & (1<<31)) ) { *(unsigned long long *) &SCR1_TIMER->MTIMECMP = *(unsigned long long *) &SCR1_TIMER->MTIME + 100; if (xTaskIncrementTick() != pdFALSE) vTaskSwitchContext(); } else { ext_trap_handler(); } }
Здесь мы перезагружаем регистры сравнения таймера в случае если сработал таймер, а если сработало иное пользовательское прерывание, то мы отправляемся на его обработку в ext_trap_handler. В исходном проекте Синтакора отсутствовал переход на пользовательские прерывания.
Так же ради экономии места следует избавиться от всех использований printf в этом файле (закомментировать их).
Настройки среды
Для успешной работы компилятора нужно прописать пути. Открываем свойства проекта и заходим в опции компилятора. И вносим директории, чтобы выглядело так:
Так же настраиваем пути для ассемблера:
И макрос для определения функции обработчика прерываний системного таймера (оно же общее прерывание)
После этих правок проект должен собираться без ошибок, но это еще не всё.
Настройка линкера
По умолчанию в проекте есть подключенный скрипт линкера. Но он опять же адресуется в папку shared.
Поэтому рекомендуется создать папку в своем проекте, скопировать туда нужный скрипт (мы будем использовать скрипт для загрузки прошивки в ОЗУ)
Далее добавляем библиотеки c и gcc в опции линкера для корректной работы таких функций как memcpy и ряда системных утилит:
Оптимизация на размер
Так как памяти мы выделили на прошивку всего 8кБ, то следует активировать оптимизацию кода по размеру, выставив такие опции:
Добавление потоков и запуск ОСРВ
Это последний шаг. Для демонстрации минимальных возможностей в этом примере создается два потока, в одном из которых не происходит ничего - находится в бездействии. Вторая задача ожидает освобождения семафора. В прерывании от линии GPIO (от кнопки) происходит активация семафора. В следующий момент после его активации во вторая задача активируется и на 50мс засвечивает светодиод.
#include <mcu32_memory_map.h> #include <pad_config.h> #include <gpio.h> #include <power_manager.h> #include <gpio_irq.h> #include <epic.h> #include <scr1_csr_encoding.h> #include "riscv_csr_encoding.h" #include "FreeRTOS.h" #include "task.h" #include "semphr.h" SemaphoreHandle_t xSemaphore; void ext_trap_handler(void) { GPIO_IRQ->CLEAR = 0xFFFF; EPIC->CLEAR = 0xFFFF; xSemaphoreGiveFromISR(xSemaphore, NULL); } static void test_task1(void *param) { while (1) { vTaskDelay(500); } } static void test_task2(void *param) { while(1) { if ( xSemaphore && xSemaphoreTake(xSemaphore, 100) == pdTRUE ) { GPIO_2->OUTPUT |= (0b1)<<(7); vTaskDelay(50); GPIO_2->OUTPUT &= ~((0b1)<<(7)); } } } void main() { PM->CLK_APB_P_SET = PM_CLOCK_GPIO_0_M | PM_CLOCK_GPIO_1_M | PM_CLOCK_GPIO_2_M | PM_CLOCK_GPIO_IRQ_M; PM->CLK_APB_M_SET = PM_CLOCK_PAD_CONFIG_M | PM_CLOCK_WU_M | PM_CLOCK_PM_M | PM_CLOCK_EPIC_M; for (volatile int i = 0; i < 10; i++) ; // LED init PAD_CONFIG->PORT_2_CFG |= (0b01)<<(7*2); GPIO_2->DIRECTION_OUT = 1<<(7); // led on shortly GPIO_2->OUTPUT |= (0b1)<<(7); for (long int i = 0; i < 3*100000l; i++) __asm volatile( "ADDI x0, x1, 0"); // это инструкция-замена NOP GPIO_2->OUTPUT &= ~((0b1)<<(7)); for (long int i = 0; i < 3*100000l; i++) __asm volatile( "ADDI x0, x1, 0"); // это инструкция-замена NOP write_csr(mtvec, 0x02000000); // конфигурация адреса вектора прерывания // extint on user key setup begin PAD_CONFIG->PORT_2_CFG |= (0b01) << (6*2); GPIO_2->DIRECTION_IN = 1<<(6); // GPIO_IRQ->LEVEL_SET = 1; GPIO_IRQ->EDGE = 1 << 2; GPIO_IRQ->LEVEL_SET = 1 << 2; // GPIO_IRQ->ANYEDGE_SET = 1; GPIO_IRQ->CFG = 9 << (2*4); GPIO_IRQ->ENABLE_SET = 1 << 2; EPIC->MASK_LEVEL_SET = 1 << EPIC_GPIO_IRQ_INDEX; // extint on user key setup /* Create work threads */ xTaskCreate(test_task1, "Task1", 128, ( void * ) 1, tskIDLE_PRIORITY + 1 , NULL ); xTaskCreate(test_task2, "Task2", 128, ( void * ) 1, tskIDLE_PRIORITY + 1 , NULL ); xSemaphore = xSemaphoreCreateBinary(); vTaskStartScheduler(); }
Так же рекомендуется всегда явно прописывать адрес вектора прерывания.
write_csr(mtvec, 0x02000000);
Выводы
Образ примера получился размером 5.5кБ и работает он из ОЗУ (для памяти программ выделено 8кБ, для памяти данных тоже 8кб). И его можно с тем же успехом запустить из встроенной энергонезависимой памяти, для этого следует откорректировать расположение памяти программ в файле .ld. И само собой, основная Модель памяти для ОСРВ будет вариант с внешней QSPI флэш.
Некоторые неудобства причиняет отсутствие встроенного векторного контроллера прерываний, но это так же и неудобство и для приложений без применения операционных систем.