Skip to content

porting_msp430_ru

Serge Vakulenko edited this page Aug 26, 2015 · 2 revisions

Микроконтроллеры MSP430

Описана последовательность работ по переносу системы uOS на архитектуру MSP430, применительно к микроконтроллеру MSP430F149.

MSP430 представляет собой 16-битную архитектуру, новую для uOS. В первую очередь требуется реализация машинно-зависимой части микроядра для архитектуры MSP430. Затем необходимо реализовать работу с периферийными устройствами: инициализацию, таймер, UART и т.д.

Полезные ссылки:

1. Создаём каталог целевой платформы

Создаем каталог uos/examples/msp430-easyweb2. Помещаем туда файлы, необходимые для компиляции.

mkdir uos/examples/msp430-easyweb2
cd uos/examples/msp430-easyweb2
cp ../arm-s3c4530/target.cfg .

В файле target.cfg изменяем значения переменных:

  • ARCH - msp430.
  • MODULES - необходимо убрать s3c4530.
  • CFLAGS - задаем тип платформы -DMSP430.
  • BINDIR - устанавливаем правильный путь к компилятору, в моём случае это /usr/local/msp323/bin.
  • CC, AR и прочие - устанавливаем имена компонентов компилятора: msp430-gcc и т.п.
  • STARTUP - стартовый код разместим в файле startup.S.

В качестве скрипта линкера используем /usr/local/msp323/msp430/lib/ldscripts/msp430x147.x.

2. Создаём файл описания специальных регистров процессора

Создаём каталог uos/sources/runtime/msp430. Помещаем в него файлы io.h, iomacros.h, msp430x14x.h, и прочие из проекта MSPGCC с описанием регистров платформы MSP430.

В файл iomacros.h помещаем определения inline-функций для доступа к аппаратным регистрам. Там уже имеются макросы READ_SR, WRITE_SR(), READ_SP и WRITE_SP. Переделываем их следующим образом:

  • int msp430_get_stack_pointer () - чтение значения регистра стека SP
  • void msp430_set_stack_pointer (int) - установка значения регистра стека SP
  • void msp430_intr_disable (int *) - сохранение статуса и запрет прерываний
  • void msp430_intr_restore (int) - восстановление статуса прерываний
  • void msp430_intr_enable () - разрешение прерываний

3. Создаём стартовый файл

На основе gcrt0.S из проекта MSPGCC создаём стартовый файл startup.S.

4. Делаем функцию начальной инициализации

Создаём файл uos/sources/runtime/msp430/init.c, содержащий функцию _init_(). Она должна:

  • устанавливать начальное состояние регистров микроконтроллера;
  • копировать инициализированные данные из памяти Flash в ОЗУ;
  • обнулять сегмент BSS;
  • вызывать функцию main(). Как образец можно использовать аналогичный файл для архитектуры ARM: runtime/arm/init.c.

5. Создаем архитектурозависимые файлы стандартной библиотеки

Создаём файлы sources/runtime/msp430/types.h, sources/runtime/msp430/stdlib.h и sources/runtime/msp430/string.h:

cd uos/sources/runtime/msp430/
cp ../arm/types.h ../arm/stdlib.h ../arm/string.h .

В начале файла sources/runtime/arch.h в соответствующее место добавляем фрагмент:

#elif defined (MSP430)
#       include <stdarg.h>
#       include <runtime/msp430/types.h>
#       include <runtime/msp430/string.h>
#       include <runtime/msp430/stdlib.h>
#       include <runtime/msp430/io.h>
#       include <runtime/ctype.h>
#       define __BYTE_ORDER __LITTLE_ENDIAN
#       define __FLOAT_WORD_ORDER __LITTLE_ENDIAN

Теперь можно проверить, что файл init.c правильно компилируется:

/usr/local/msp323/bin/msp430-gcc -c -fno-builtin -DMSP430 -I../.. init.c

6. Делаем функции отладочной выдачи на консоль

В файле uos/sources/runtime/msp430/debug.c создаём функции отладочной выдачи на консоль: debug_putchar(), debug_getchar(), debug_peekchar().

7. Первый тест: отладочная выдача на консоль

В каталоге uos/examples/msp430-easyweb2 создаём файлы test_debug.c:

#include <runtime/lib.h>

int main (void)
{
        for (;;) {
                debug_printf ("Hello, World!\n");
                debug_getchar();
        }
}

и Makefile:

TARGET          = $(CURDIR)
OS              = $(shell cd ../..; pwd)
include target.cfg

TESTS           = test_debug.hex

all:            $(TESTS)
                for f in *.out; do $(SIZE) $$f; done
clean:
                rm -rf *~ *.[oasi] *.out *.hex *.lst *.dis .deps $(MODULES) $(TESTS)

include $(OS)/sources/rules.mak

Запускаем "make". В результате получаем файл test_debug.hex, который загружаем в ПЗУ целевой платы. При включении на консоль должна выдаваться строка "Hello, World" (скорость 115200, 8 бит, без чётности).

8. Проектируем структуру контекста

Определяем количество и порядок регистров, сохраняемых в стеке при входе в прерывание или исключение. Документируем на будущее.

9. Создаём обработчики исключений

В файл init.c добавляем функции _pagefault_handler_(context) и _exception_handler_(context). Они должны печатать на отладочную консоль значения регистров и выполнять аппаратный сброс системы.

10. Перенос ядра

Создаём каталог uos/sources/kernel/msp430 и в нём include-файл machdep.h с описанием машинно-зависимых функций и макросов для платформы MSP430:

  • ARCH_INTERRUPTS - количество аппаратный прерываний, 15 для MSP430
  • arch_state_t - тип для хранения маски аппаратных прерываний, int для MSP430
  • arch_stack_t - тип для хранения указателя стека, void * для MSP430
  • arch_get_stack_pointer () - inline-функция, возвращающая текущий указатель стека
  • arch_set_stack_pointer () - inline-функция, устанавливающая указатель стека
  • arch_intr_disable () - inline-функция, запрещающая аппаратные прерывания
  • arch_intr_restore () - inline-функция, восстанавливающая режим аппаратных прерываний
  • arch_intr_bind () - inline-функция, пустая для MSP430
  • arch_intr_unbind () - inline-функция, пустая для MSP430
  • arch_idle () - inline-функция, переводящая процессор в режим ожидания

В файл machdep.c помещаем код машинно-зависимых функций:

  • arch_task_switch () - функция, переключающая выполнение на другую задачу
  • _interrupt_handler_ () - обработчик аппаратного прерывания, вызываемый из startup-кода
  • arch_intr_allow () - функция, открывающая маску указанного аппаратного прерывания
  • arch_build_stack_frame () - функция, создающая фрейм в стеке для старта новой задачи

11. Второй тест: формирование задач

В каталоге uos/examples/msp430-easyweb2 создаём файл test_task.c:

#include <runtime/lib.h>
#include "kernel/uos.h"

ARRAY (task, 0x400);

void hello (void *arg)
{
        for (;;) {
                debug_printf ("Hello from `%s'!\n", arg);
                debug_printf ("Task space %d bytes, free %d bytes\n",
                        sizeof (task), task_stack_avail ((task_t*) task));
                debug_printf ("(Press Enter)\n");
                debug_getchar ();
        }
}

void uos_init (void)
{
        debug_puts ("\nTesting task.\n");
        task_create (hello, "task", "hello", 1, task, sizeof (task));
}

В файле Makefile устанавливаем TESTS = test_task.hex. Запускаем "make". В результате получаем файл test_task.hex, который загружаем в ПЗУ целевой платы. При включении на консоль должна выдаваться строка "Hello from `task'" и информация о свободной части стека задачи.

12. Драйвер таймера

В начале файла sources/timer/timer.c добавляем объявление номера прерывания таймера:

#if MSP430
#   define TIMER_IRQ	(TIMERA0_VECTOR / 2)
#endif

В функции timer_init() в соответствующее место добавляем инициализацию регистров таймера:

#if MSP430
        TACTL = 0;
        TACTL = TASSEL_1;
        TACCR0 = t->khz * t->msec_per_tick / 2;
        TACTL |= TACLR;
        TACTL |= MC_1;
#endif

13. Третий тест: таймер

В каталоге uos/examples/msp430-easyweb2 создаём файл test_timer.c:

#include <runtime/lib.h>
#include "kernel/uos.h"
#include "timer/timer.h"

ARRAY (task, 0x400);
timer_t timer;

void hello (void *arg)
{
        for (;;) {
                debug_printf ("Hello from `%s'! msec = %d\n",
                        arg, timer_milliseconds (&timer));
                mutex_wait (&timer.decisec);
        }
}

void uos_init (void)
{
        debug_puts ("\nTesting timer.\n");
        timer_init (&timer, KHZ, 1000);
        task_create (hello, "task", "hello", 1, task, sizeof (task));
}

В файле Makefile устанавливаем TESTS = test_timer.hex. Запускаем "make". В результате получаем файл, который загружаем в ПЗУ целевой платы. При включении на консоль должна выдаваться строка "Hello from `task'" и счётчик миллисекунд.

14. Драйвер асинхронного порта

В начале файла sources/uart/uart.h в соответствующее место добавляем определение размера стека задачи асинхронного порта:

#if MSP430
#   define UART_STACKSZ 0x180

Аналогично в файле протокола SLIP sources/uart/slip.h:

#if MSP430
#   define SLIP_STACKSZ 0x180

В начале файлов sources/uart/uart.c и sources/uart/slip.c добавляем включение файла макросов для MSP430:

#if MSP430
#   include "msp430.h"
#endif

Создаём файл sources/uart/msp430.h с макросами UART для MSP430:

#define RECEIVE_IRQ(p)			(USART0RX_VECTOR/2)
#define TRANSMIT_IRQ(p)			(USART0TX_VECTOR/2)

#define enable_transmitter(p)		(U0ME |= UTXE0)
#define disable_transmitter(p)		(U0ME &= ~UTXE0)
#define test_transmitter_enabled(p)	(U0ME & UTXE0)

#define enable_receiver(p)		(U0ME |= URXE0)
#define disable_receiver(p)		(U0ME &= ~URXE0)

#define enable_receive_interrupt(p)	(U0IE |= URXIE0)
#define enable_transmit_interrupt(p)	(U0IE |= UTXIE0)
#define disable_transmit_interrupt(p)	(U0IE &= ~UTXIE0)

#define transmit_byte(p,c)		TXBUF0 = (c)
#define get_received_byte(p)		RXBUF0

#define test_transmitter_empty(p)	(UTCTL0 & TXEPT)
#define test_receive_data(p)		(U0IFG & (URXIFG0 | 1))
#define test_get_receive_data(p,d)	((U0IFG & (URXIFG0 | 1)) ? \
					((*d) = RXBUF0, 1) : 0)

#define test_frame_error(p)		(URCTL0 & FE)
#define test_parity_error(p) 		(URCTL0 & PE)
#define test_overrun_error(p) 		(URCTL0 & OE)
#define test_break_error(p)		(URCTL0 & BRK)

#define clear_frame_error(p)		(URCTL0 &= ~FE)
#define clear_parity_error(p)		(URCTL0 &= ~PE)
#define clear_overrun_error(p)		(URCTL0 &= ~OE)
#define clear_break_error(p)		(URCTL0 &= ~BRK)

15. Четвёртый тест: асинхронный порт

В каталоге uos/examples/msp430-easyweb2 создаём файл test_uart.c:

#include <runtime/lib.h>
#include "kernel/uos.h"
#include "uart/uart.h"

ARRAY (task, 0x400);
uart_t uart;

void hello (void *data)
{
        for (;;) {
                puts (&uart, "\nHello, World! ");
                getchar (&uart);
        }
}

void uos_init (void)
{
        debug_puts ("\nTesting UART.\n");
        uart_init (&uart, 0, 90, KHZ, 115200);
        task_create (hello, 0, "hello", 1, task, sizeof (task));
}

В файле Makefile устанавливаем TESTS = test_uart.hex. Запускаем "make". В результате получаем файл, который загружаем в ПЗУ целевой платы. При включении на консоль должна выдаваться строка "Hello, World".

16. Комплексный тест: пять обедающих философов

Проведём комплексную проверку микроядра, драйвера таймера и драйвера асинхронного порта. В каталоге uos/examples/msp430-easyweb2 создаём файл examples/msp430-easyweb2/philosophers.c. В нем помещаем код решения классической задачи пяти обедающих философов.

В файле Makefile устанавливаем TESTS = test_philosophers.hex. Запускаем "make". В результате получаем файл, который загружаем в ПЗУ целевой платы. При включении на консоли будут отображаться пять философов и изменение их состояния во времени.

Перенос системы uOS на архитектуру MSP430 можно считать завершённым.

You can’t perform that action at this time.