Изучение ассемблера на PIC32: моргаем светодиодом

PIC
Постижение программирования под микроконтроллеры требует, если не навыка работы с ассемблером, то хотя бы понимания ассемблера. Я буду использовать Arduino совместимую плату, чтобы показать, как работать с ассемблером на контроллерах PIC32. Начну, по традиции, с мигающего светодиода, который уже распаян на плате.

Оборудование
Для работы я буду использовать широко доступную плату Digilent ChipKit Uno32.
Эта плата является PIC32 клоном Arduino. Таким образом плата должна быть готова к работе сразу после подключения платы к мини USB разъему.

Прошивка и настройка окружения
Со стороны большого брата есть инструмент MPIDE. Это и текстовый редактор, и компилятор, и программа для прошивки, и библиотека Arduino-совместимого кода.
В установочной директории мы можем найти обычные иструменты для разработки:
Здесь и далее я буду приводить пути для Mac OS X, но возможно использовать windows или linux.
  • gcc: компилятор C, линковщик, ассемблер и прочие утилиты
    /Applications/mpide.app/Contents/Resources/Java/hardware/pic32/compiler
  • avrdude — программа для работы с загрузчиком
    /Applications/Mpide.app/Contents/Resources/Java/hardware/tools/avr/bin/avrdude


Сразу после включения платы запускается загрузчик.
  • ждем пару секунд
  • если в это время было подключение от avrdude
  • заливаем прошивку во флеш


Берем в качестве основы минимальное C приложение.
https://github.com/rkujawa/chipKIT-minimal-application.git

Makefile содержит правило для сборки main.c, для начала заменим main.c на light.S
*.S в отличии от *.s позволяет использовать C препроцессор (например #include).

$(OBJDIR)/main.o:
        $(CC) $(CFLAGS) -o $(OBJDIR)/main.o light.S


Пример C кода расчитан на плату chipkit max32, нужно:
  • задать тип процессора 32MX320F128H
  • выбрать другой скрипт линковки chipKIT-UNO32-application-32MX320F128L.ld
  • выбрать другой тип в avrdude (параметр -p?)

/Applications/Mpide.app/Contents/Resources/Java/hardware/tools/avr//bin/avrdude  -C/Applications/Mpide.app/Contents/Resources/Java/hardware/tools/avr//etc/avrdude.conf -c stk500v2 -p?


Нашему микроконтроллеру соответствует pic32-360.
Последним шагом подготовки, вбиваем путь к gcc, avrdude и задаем COM порт.

TOOLCHAIN_PREFIX=/Applications/mpide.app/Contents/Resources/Java/hardware/pic32/compiler/pic32-tools
# Path to chipKIT avrdude
AVRTOOLS_PREFIX=/Applications/Mpide.app/Contents/Resources/Java/hardware/tools/avr/
# Serial port used to upload image with avrdude
SERIAL_PORT=/dev/cu.usbserial-A1019FQ7


Что мы оставляем за кадром?
  • настройку микроконтроллера сразу после старта
  • настройку прерываний
  • обработчика прерывания reset
  • настройку стека

Этой работой занимается startup/crt0.S

Приступаем к написанию кода.
В любом текстовом редакторе редактируем файл light.S

Объявляем функцию main:
# Зажечь лампочку на ножке RF0 (LD5) ассемблером
main:   .global main                # Помечаем метку main как глобальную


Разрешаем использование ноги RF0 как выводного контакта.
Здесь надо сделать небольшое лирическое отступление.
Каждая команда из набора PIC32 занимает строго 32бита.
Мы не можем поместить 32битное число или адрес в регистр за одну команду.

«Регистры» управления переферией находятся по базовому адресу 0xBF800000.
Наш GPIO блок F находится 0xbf880000.

Если первый пин GPIO блока F должен быть выводным, то нам необходимо включить первый бит по адресу: 0xbf886144

Мы будем делать это в три этапа:
  1. Загрузка базового смещения 0xbf880000 в регистр под номером 2
  2. Загрузка числа 1 (первый бит включен) в регистр 3
  3. Запись регистра 3 по базовому смещению из регистра 2 и относительному смещению 0x6144

B терминах C это:
*(volatile unsigned*)(0xBF880000 + (0x6144)) = 1;


# Включаем ножку как ножку выхода
	li	$2,0xbf880000
	li	$3,1			# 0x1
	sw	$3,0x6144($2)


Вторая часть нашей программы должна:
  1. крутиться в бесконечном цикле
  2. Включать ножку RF0
  3. Ждать какой-то промежуток времени (где-то около секунды)
  4. Выключать ножку RF0
  5. Ждать какой-то промежуток времени

Включение ножки производится путём записи единицы в соответствующий бит регистра по адресу 0xBF886168 а выключение 0xBF886164.

Как происходит ожидание одной секунды?
Наш микроконтроллер должен работать на частоте 80MHz.
Т.е. при выполнении одной команды 80 миллионов раз в секунду у нас будет задержка не отличающаяся на порядок от секунды.
Экспериментальным путём я нашел что нужно около пятнадцати миллионов раз в секунду выполнить две команды:
  1. отнять 1 от счетчика, или эквивалентно, добавить минус один
  2. сравнить счетчик с нулем, при ненулевом значении, вернуться на 1.

# 14M ~ 1s  0x0E20E5E = 0x0E20000 + 0x0E5E
        li      $2,0x0E20000
        addiu   $2,$2,0x0E5E
.delay:
        addiu   $2,$2,-1
        bne     $2,$0,.delay


Конечный вариант:
# Зажечь лампочку на ножке RF0 ассемблером
main:   .global main                # Помечаем метку main как глобальную
# Включаем ножку как ножку выхода
	li	$2,0xbf880000
	li	$3,1			# 0x1
	sw	$3,0x6144($2)

.loop:

# зажигаем ножку
        li      $2,0xBF880000
        li      $3,1
        sw      $3,0x6168($2)

# ждём одну секунду
	# 14M ~ 1s  0x0E20E5E = 0x0E20000 + 0x0E5E
        li      $2,0x0E20000
        addiu   $2,$2,0x0E5E

.delay:
        addiu   $2,$2,-1
        bne     $2,$0,.delay

# гасим ножку
        li      $2,0xBF880000
        li      $3,1
        sw      $3,0x6164($2)

# ждём одну секунду
	# 14M ~ 1s  0x0E20E5E = 0x0E20000 + 0x0E5E
        li      $2,0x0E20000
        addiu   $2,$2,0x0E5E

.delay1:
        addiu   $2,$2,-1
        bne     $2,$0,.delay1  

    j	    .loop


Если все сделано правильно, то после команды make контроллер будет весело моргать светодиодом.
  • +2
  • 11 марта 2014, 07:55
  • ihanick
  • 1
Файлы в топике: uno32-asm-blink.zip

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

RSS свернуть / развернуть
Это бы в PIC перенести.
0
PIC32 прошивается дудкой?! Или там ардуино-совместимый бутлоадер?
0
  • avatar
  • Vga
  • 11 марта 2014, 23:07
Арудино совместимый бутлоадер + конфигурационные файлы от производителя.
Можно писать такой же код как в Арудино, правда, только из своего IDE.

void setup() {                
  pinMode(PIN_LED2, OUTPUT);     
}
void loop() {
  digitalWrite(PIN_LED2, HIGH);   // set the LED on
  delay(1000);              // wait for a second
  digitalWrite(PIN_LED2, LOW);    // set the LED off
  delay(1000);              // wait for a second
}
0
… это MIPS-овский ассемблер?
0
Да, это mips32. Во второй и третьей части это упоминается.
0
Что мы оставляем за кадром?
настройку микроконтроллера сразу после старта
настройку прерываний
обработчика прерывания reset
настройку стека

Хотелось бы последующие статьи про то что за «кадром».
И в формате совсем для «начинающих».
Спасибо.
0
Линковка в четвёртой части
0
Запустил программу, но частота моргания диода очень медленная.
Думаю проблема с настройкой SYSCLOCK.
Кварц 8МГц.
Настройки:
FNOSC = 011 Primary Oscillator (POSC) with PLL module (XT+PLL, HS+PLL, EC+PLL
POSCMOD = 01 XT Oscillator mode
FPBDIV = 01 PBCLK is SYSCLK divided by 2 ставил = 00 PBCLK is SYSCLK divided by 1 на взгляд не изменилось
FPLLMUL = 101 20x multiplier
FPLLODIV = 000 PLL output divided by 1


Что за режимы XT, HS, EC для POSCMOD?
Подскажите где может быть ошибка.
Спасибо.
0
Про режимы — таблица 6.3 в DS61112E-page 6-21.
Имхо, лучше всего распечатать схему тактирования на Рисунке 6-1 в DS61112E-page 6-2, непосредственно поверх неё ручкой просчитать как хочется тактировать и выставить соответственно значения регистров.
0
Спасибо.
Сколько смотрел, а про режимы не заметил.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.