MSP430 - учебный курс.

Добрый день всем!

Позвольте представить вам учебный курс по микроконтроллерам MSP430.

Хочу сразу предупредить — данный тип МК несколько тяжеловат для рядового пользователя. Его используют в основном профессиональные разработчики, но… Я хочу сделать его доступным для всех. Так, что смотрим и не боимся.
Да, и ещё — традиционно я тяготею к ассемблерным типам языков, так что начнём с них. Потом будет и С.

Как уже говорил глубокоуважаемый _YS_, данный тип контроллеров построен по Фон-Неймановской архитектуре, т.е. имеет одну адресную ось. Деления на память программ и данных нет, просто они располагаются по разным адресам. Чуть позже приведу разбивку.
Мануалы пока читать не будем, просто покажу основное.
Контроллер 16-ти разрядный, в составе ядра есть 16 регистров, четыре из которых специального назначения (R0 или PC — счетчик команд, R1 или SP — указатель стека, R2 или SR — регистр состояния, R3 или CG — генератор констант). Остальные (R4-R15) доступны как регистры общего назначения. Есть одна отличная вещь — любой регистр может быть указателем.
Пока сведений достаточно.

С чего начать? Со среды программирования, ибо первые шаги можно делать не имея аппаратуры.

Внимание! Кто захочет начать с аппаратуры — запомните! Контроллер питается от напряжения 3,3 вольта, 5 вольт для него смертельны! Будьте осторожны.

Итак, среда разработки. Во всём мире профессионалы пользуются программами фирмы IAR Systems — www.iar.com.
Не будем изобретать велосипед, последуем их рекомендациям. С официального сайта можно скачать 30-дневную бесплатную версию. Заморочка в том, что прийдётся заполнить длинную форму личных данных. Кто не боится — вперёд!

После установки и запуска мы видим (у меня 4-я версия, текущая на официальном сайте — 5-я, но отличий немного):

Посмотреть на Яндекс.Фотках

Жмём кнопку «Создать новый проект в текущем рабочем пространстве» («Create new project in current workspace»).
Смотрим на окошко и выбираем «asm/asm».

Посмотреть на Яндекс.Фотках

Нажимаем «Ok» сохраняем файл проекта в нужное нам место. Рекомендую, каждый проект сохранять в отдельный каталог.

Получаем следующее:

Посмотреть на Яндекс.Фотках

Комментирую полученный код.


#include "msp430.h"

Ну тут всё ясно, подключаем заголовочный файл.


        NAME    main                    ; module name

        PUBLIC  main                    ; make the main label vissible
                                        ; outside this module

Это стандартное объявление имени модуля и его видимости из других модулей.


        ORG     0FFFEh
        DC16    init                    ; set reset vector to 'init' label

Тут мы объявляем вектор перехода по сигналу «Reset». Я делаю это несколько иначе, чуть ниже покажу как.
У MSP430 вектора прерываний располагаются в конце памяти — от адреса FFFFh и вниз. Для серии F1xxx и F1xx их 16 штук. Да, и ещё существенное отличие — в вектор заносится адрес, а не команда перехода! Т.е., в данном случае, адрес метки «init».


        RSEG    CSTACK                  ; pre-declaration of segment
        RSEG    CODE                    ; place program in 'CODE' segment

Тут объявляем сегмент стека и кода.
Вот собственно с этого и начинается программа.


init:   MOV     #SFE(CSTACK), SP        ; set up stack

Вот она метка «init», её адрес компилятор потом подставит в вектор сброса.
Первая команда — инициализация стека. Директива SFE вычисляет конец сегмента. А # нужен, чтобы подставлять вычисленное значение, а не рассматривать его как адрес. К примеру:


mov   #1, R1 - поместить 1 в R1,
а
mov   1, R1 - поместить значение из ячейки памяти с адресом 1 в R1

Вот такие хитрости.
Идём далее.
Команда «nop» у нас просто для задержки.
А вот это важная вещь:

        MOV.W   #WDTPW+WDTHOLD,&WDTCTL  ; Stop watchdog timer

Дело в том, что по умолчанию у MSP430 включен сторожевой таймер. Чтобы не было неожиданностей, выключаем его данной командой.
Да, и обратите внимание: все команды пишутся немного не так как у AVR, x51 или x86 — источник сначала, затем приёмник.


        JMP $                           ; jump to current location '$'
                                        ; (endless loop)

Это прыжок «сам на себя». Просто, чтобы создать замкнутый цикл.

Ну и не забываем «END» в конце программы, также как и у PIC в MPLAB. После этого волшебного слова программа заканчивается.

Что сейчас делает наша программа? Почти ничего. Давайте наполним её смыслом. А в качестве примера сделаем сортировку случайных чисел.
Задача следующая:
1. Имеем данные в ПЗУ (набор из 16 случайных чисел).
2. Переместим данные из ПЗУ в ОЗУ.
3. Отсортируем данные в ОЗУ.

Для начала настроим проект.
Выберем тип контроллера. Открываем меню «Projects->Options»:

Посмотреть на Яндекс.Фотках

Появится вот такое окошко, где мы и выберем тип контроллера.

Посмотреть на Яндекс.Фотках

Далее выберем тип отладчика:

Посмотреть на Яндекс.Фотках
Счастливые владельцы аппаратного эмулятора могут выбрать «FET Debugger», ну а нам пока сойдет и «Simulator».

Для начала настроек достаточно. Жмём «Ok».

Далее, вставляем вместо всего в окошке текста следующий код (пока без самой сортировки):

#include "msp430.h"                     ; подключаем заголовочный файл

        NAME    main                    ; имя модуля
        PUBLIC  main                    ; установить видимость модуля из других
                                        
; Определение констант
ArrLn	EQU	16                      ; длина массива

        ; Вектора прерываний
        RSEG	INTVEC
        ORG     RESET_VECTOR           ; вектор сброса
        DC16    init 

        RSEG    CSTACK                  ; объявление сегмента стека

        RSEG    CODE                    ; объявление сегмента кода

init:   MOV     #SFE(CSTACK), SP        ; инициализация регистра стека

main:                                   ; начало основной части
        MOV.W   #WDTPW+WDTHOLD,&WDTCTL  ; запрет сторожевого таймера
        
SortMain:
        ; Переписать из ПЗУ в ОЗУ массив данных
        mov	#ArrLn, R4              ; инициализируем счётчик количества данных
	mov	#RamArr, R5             ; установим R5 как указатель на данные в ОЗУ
	mov	#RomArr, R6             ; установим R6 как указатель на данные в ПЗУ
	
MoveRomToRam:
	mov	@R6+, 0(R5)             ; копирование слова
	add	#2, R5                  ; продвинем указатель
	dec	R4                      ; уменьшим счётчик
	jnz	MoveRomToRam            ; если не ноль, то повторим
   
BasicCycle:
	JMP $                           ; jump to current location '$'

	
;	определение данных в ОЗУ
	RSEG	DATA16_N
RamArr:
	DS16	ArrLn

;	определение данных в ПЗУ
	RSEG	DATA16_C
RomArr:
	DC16	101, 11, 25, 657, 567, 217, 5732, 896, 123, 5467, 12, 65, 2345, 23, 98, 2398


        END


Разберём по частям. Но для начала научимся запускать программу на выполнение.
Скомпилируем проект — пункт меню «Project -> Make» или значок на панели инструментов:

Появится окошко «Messages», в котором отражен процесс компиляции. В случае успеха (а данный текст должен нормально скомпилироваться) появится сообщение об этом.

Далее жмем «Project -> Debug» или кнопку:

Должен запуститься собственно отладчик. IAR поддерживает два набора окон — раздельно для редактирования и отладки.
Закроем лишнее. Структура проекта при отладке не нужна, окно «Disassembly» используется редко, да и «Messages» не нужно.
А вот регистры и память хотелось бы видеть. Жмём «View -> Registers» и два раза «View -> Memory». Перетаскиваем окошки в нужные углы и получаем картинку (на мой вкус, можете сделать по своему):

Посмотреть на Яндекс.Фотках
В окошках «Memory» выбираем типы памяти «RAM» и «Flash», так будет удобнее просматривать. Также, в каждом окшке памяти щёлкаем правой кнопкой и выбираем из контекстного меню «2x Units» (для просмотра 16-ти разрядных данных) и «Data Coverage -> Enable» (будет показано обращение к текущим ячейкам — удобно).
К отладке готовы!

С помошью кнопки F10 (не входить в подпрограммы) или F11 (входить в подпрограммы) проходим по шагам и любуемся результатом. Техника нам подвласна!

На сегодня всё, а завтра продолжим разбор программы.

Просьба — прокомментируйте. Понравилось, не понравилось, не слишком ли тяжело для понимания, что хотелось бы ещё в этом уроке, а также что хочется на будущие. Жду откликов.
Спасибо за внимание.

  • +2
  • 08 марта 2011, 12:39
  • SerjT

Комментарии (9)

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

А теперь — читаю…
0
  • avatar
  • _YS_
  • 08 марта 2011, 13:31
Прочел. Комментарии — в письме.

В целом хочу сказать — отличная статья, спасибо.
0
  • avatar
  • _YS_
  • 08 марта 2011, 13:59
Непонятно что такое DC16 в векторе? Это какая то директива или метка?

Ну и разбор программы лучше бы сделать в этой же статье, чтобы статьи были законченными, каждая сама в себе. А то получил порцию информации и встал.
0
Судя по всему — это 16-битная константа.
0
Да, это резервирование памяти. Как .DB в AVR.

Ну, кагбэ D[efine] C[onstant] 16 [bits]

Принципиалная разница состоит в том, что в AVR предполагается, что по адресу вектора прерывания раполагается непосредственно первая команда обработчика, которая, естесственно, по факту замещается rjmp'ом на настоящий обработчик. Тут же мы по адресу вектора кладем адрес настоящего обработчика т.е., просто резервируем константу.
0
Доброго времени суток! Попробовал скомпилировать проект, перенабранный вручную из этой статьи. Компилятор выдал следующее: «Error[40]: Bad instruction C:\...\asm.s43 6
Error[50]: Undefined symbol:'ArrLn' C:\...\asm.s43 6
Error while running Assembler»
Поискал причину ошибки в раскладке клавиатуры при вводе служебных символов и букв, внешне одинаковых для латиницы и кириллицы. Их взаимозамена не изменила реакцию компилятора. «Поиграл» пробелами. Скопировал строку «ArrLn EQU 16; длина массива» из примера в IAR (использую версию 6.3.11.2079). Компилятор настаивает на своём. Где же некорректность?
0
Довольно странная штука была обнаружена в ходе мигания диодами.
Стандартное состояние битов регистра P1OUT по умолчанию «0» и сбрасывается после ресета, в вот стандартное состояние битов регистра P2OUT по умолчанию почему то «1». Может я чего то не знаю, но на моя взгляд это странно. учитывая что в документации по MSP430x2xx об этом ни слова.
0
А не надо уповать на стандартное и предустановленное.
Все регтистры надо инициализировать принудительно и явно.
Тогда не будет недоразумений.
0
Совершенно верно, не стоит. Я уже не раз нарывался, что значения по умолчанию не всегда соответствуют документации.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.