Я работаю над голым металлическим заданием Raspberry Pi 3 (RPi3) для школы и пытаюсь получить код сборки, чтобы изменить яркость каждого диода в светодиоде RGB.
Проще говоря, я пытаюсь заставить широтно-импульсную модуляцию (ШИМ) работать на моем RPi3 с использованием ассемблерного кода и без библиотек. После исследования (подробности ниже), я думаю, я собрал все шаги, необходимые для настройки ШИМ для использования на GPIO 18 и 19. Но, используя мой код, светодиод даже не горит. Есть ли какие-то шаги, которые я пропускаю или что-то не вижу в моем коде?
Принятые шаги:
- Я упростил свой код до процедуры сверху вниз, за исключением одной
подпрограмма _wait, которая проверена на работоспособность. я хотел
устраните как можно больше потенциальных проблем, таких как неуместные указатели стека.
- Я имею в виду периферийные устройства BCM2835 {1}.
Я протестировал ШИМ на своем Pi, используя Ultibo {2} и их пример с ШИМ
с положительными результатами. Я даже прочитал / проследил их исходный код, чтобы собрать
список процедур и аппаратных адресов, которые они используют. Вот где я
придумал мой список, который я использую в коде сборки:
- Установить RNG1 и RNG2
- Установите РЕЖИМ1 и РЕЖИМ2
- Включить каналы M / S
- Установить частоту:
- Остановить часы ШИМ {a}
- Установка частоты в регистре PWM CLK DIV {b}
- Установить часы на генератор
- Запуск PWM Clock
- Установить GPIO 18 и 19 в качестве входа (это лишнее и ненужное)
- Установите GPIO 18 и 19 на альтернативную функцию 5 (переключает их на выходы ШИМ вместо входов)
- Запустить запись в бесконечный цикл в DAT1 и DAT2 с изменением значений от 0 до (максимальное значение диапазона)
Примечания:
{a} Я использовал исходный код Ultibo, чтобы найти адрес для PWM Clock, потому что его нет в BCM2835. Кроме того, согласно исходному коду Ultibo, часы PWM используют тот же дизайн, что и часы общего назначения на стр. 107 и 108 BCM2835, поэтому я буду использовать те же имена.
{b} Также использовал Ultibo для просмотра формулы для определения чисел, вводимых для DIVI и DIVF: ( Частота по умолчанию / Желаемая частота ) = Остаток DIVI DIVF. В этом примере используется значение по умолчанию 19,2 МГц и требуемая частота 9,6 МГц, что приводит к DIVI 2 и DIVF 0.
Ссылка:
{1} https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2835/BCM2835-ARM-Peripherals.pdf
{2} https://github.com/ultibohub/Examples/tree/master/18-PWMControl/RPi3
Код
.section .text
.global _start
_start:
@===============================================================
@ Pulse Width Modulator
@===============================================================
@ 1. Set Ranges
@ 2. Set Modes
@ 3. Enable M/S
@ 4. Enable Channels
@===============================================================
LDR R5, =0x3F20C000 @ R5 = CTL (PWM Control Register, See BCM2835 Pg 142-143)
LDR R6, =0x3F20C010 @ R6 = RNG1 (See BCM2835 Pg 141)
LDR R7, =0x3F20C020 @ R7 = RNG2 (All 32 bits dedicated to the value)
@ Set Ranges --------------------------------------------
MOV R1, #0xFF @ Desired Range from 0 to 255
STR R1, [R6] @ RNG1:=255
MOV R0, #10
BL _wait @ Wait 10 ms
STR R1, [R7] @ RNG2:=255
MOV R0, #10
BL _wait @ Wait 10 ms
@ Set Modes -------------------------------------------------
LDR R1, [R5] @ Get current Control Registers
BIC R1, #1<<1 @ Clear Bit 1 (MODE1:=PWM_Mode)
STR R1, [R5] @ Store back to CTL
MOV R0, #10
BL _wait @ Wait 10ms
LDR R1, [R5] @ Get current Control Registers again
BIC R1, #1<<9 @ Clear Bit 9 (MODE2:=PWM_Mode)
STR R1, [R5] @ Store bac
MOV R0, #10
BL _wait @ Wait 10ms
@ Enable M/S -----------------------------------------------
LDR R1, [R5] @ Get current Control Registers
ORR R1, #1<<7 @ Set Bit 7 (MSEN1:=1)
STR R1, [R5] @ Store back
MOV R0, #10
BL _wait @ Wait 10ms
LDR R1, [R5] @ Get current Control Registers
ORR R1, #1<<15 @ Set Bit 15 (MSEN2:=1)
STR R1, [R5] @ Store back
MOV R0, #10
BL _wait @ Wait 10ms
@ Enable Channels --------------------------------------------
LDR R1, [R5] @ Get current Control Registers
ORR R1, #1<<0 @ Set Bit 0 (PWEN1:=1)
STR R1, [R5] @ Store back
MOV R0, #10
BL _wait @ Wait 10ms
LDR R1, [R5] @ Get current Control Registers
ORR R1, #1<<8 @ Set Bit 8 (PWEN2:=1)
STR R1, [R5] @ Store back
MOV R0, #10
BL _wait @ Wait 10ms
@===========================================================
@ Clock Procedures
@===========================================================
@ 1. Stop Clock
@ 2. Set Frequency
@ 3. Set Clock to Oscillator Mode
@ 4. Start Clock
@===========================================================
LDR R5, =0x3F1010A0 @ PWM CLK CTL (Clock Control Register)
LDR R6, =0x3F1010A4 @ PWM CLK DIV (Clock Divisor Register)
LDR R7, =0x5A000000 @ PWM CLK PASSWD
@ Stop Clock ----------------------------------------------
LDR R3, [R5] @ Read PWM Clock Control Register
BIC R3, #1<<7 @ Clear BUSY bit
BIC R3, #1<<4 @ Clear ENAB bit (to turn it off)
ORR R3, R7 @ Set PASSWD
@ R3 is ready to write
STR R3, [R5] @ Store R3 to Clock Register (ENAB bit is cleared)
MOV R0, #10
BL _wait @ Wait 10ms
@ Set Frequency -------------------------------------------------
_clock_freq:
MOV R3, #2<<12 @ 19.2MHz divided by 9.6MHz = 2 remainder 0
@ Shift 12 places to Integer part of Divisor
@ (BCM2835 Pg 108 for bit-packing of general clocks)
ORR R3, R7 @ Set Password
@ R3 should now be 0x5A002000
_clock_freq_loop:
MOV R0, #10
BL _wait @ Wait 10ms each loop
LDR R1, [R5] @ Load Clock Control Register (to check BUSY bit)
LSR R1, #7 @ Move Busy Bit to Bit Position 0
AND R1, #1 @ Single out the Busy Bit
CMP R1, #0 @ Check to see if it is cleared
BNE _clock_freq_loop @ Try again if clock is busy
_clock_freq_end:
STR R3, [R6] @ Set Frequency to Clock Divisors Register
MOV R0, #10
BL _wait
@ Set Oscillator Mode --------------------------------------
_clock_osc:
LDR R3, [R5] @ Read PWM Clock Control Register
BIC R3, #1<<7 @ Clear BUSY bit
BIC R3, #0xF @ Clear SRC bits (0-3)
ORR R3, #1 @ Set SRC to Oscillator Mode
ORR R3, R7 @ Set PASSWD
@ R3 is ready to write
_clock_osc_loop:
MOV R0, #10
BL _wait @ Wait 10ms each loop
LDR R1, [R5] @ Load Clock Control Register
LSR R1, #7 @ Move Busy Bit to Bit Position 0
AND R1, #1 @ Single out the Busy Bit
CMP R1, #0 @ Check to see if it is cleared
BNE _clock_osc_loop @ Try again if clock is busy
_clock_osc_end:
STR R3, [R5] @ Store R3 to Clock Register (SRC = 1)
MOV R0, #10
BL _wait @ Wait 10ms
@ Start Clock -----------------------------------------
_clock_start:
LDR R3, [R5] @ Read PWM Clock Control Register
BIC R3, #1<<7 @ Clear BUSY bit
ORR R3, #1<<4 @ Set ENAB bit (to turn it back on)
ORR R3, R7 @ Set PASSWD
@ R3 is ready to write
_clock_start_loop:
MOV R0, #10
BL _wait @ Wait 10ms each loop
LDR R1, [R5] @ Load Clock Control Register
LSR R1, #7 @ Move Busy Bit to Bit Position 0
AND R1, #1 @ Single out the Busy Bit
CMP R1, #0 @ Check to see if it is cleared
BNE _clock_start_loop @ Try again if clock is busy
_clock_start_end:
STR R3, [R5] @ Store R3 to Clock Register (ENAB bit is set)
MOV R0, #10
BL _wait @ Wait 10ms
@======================================================
@ GPIO Procedures
@======================================================
@ 1. Set Pins 18 and 19 as Inputs
@ 2. Set to Alternate Function 5 (Overwrites Previous Step)
@======================================================
LDR R3, =0x3F200004 @ R3 = GPIO_FSEL1
@ Set Pins as Input ---------------------------------
LDR R1, [R3] @ Load current FSEL1 options
BIC R1, #0b111111<<24 @ Sets GPIOs 18 and 19 to Default (Input)
STR R1, [R3] @ Not sure why... but Ultibo does this in bcm2710.pas
MOV R0, #10
BL _wait @ Wait 10ms
@ Set Pins to Alternate Function 5 ---------------------
LDR R1, [R3] @ Load current FSEL1 options again
BIC R1, #0b111111<<24 @ Clear whatever is in FSEL for 18 and 19 (should already be clear)
ORR R1, #0b010010<<24 @ 010 010 (010 is alt function 5 for both GPIO 18 and 19)
@ Shifted over 8 sets of 3 bits
@ (8 * 3 = 24) into position for GPIO 18 and 19
STR R1, [R3] @ Store it into FSEL1
MOV R0, #10
BL _wait @ Wait 10ms
@==============================================
@ Set Data / Main Loop
@==============================================
LDR R5, =0x3F20C014 @ R3 = PWM_DAT1
LDR R6, =0x3F20C024 @ R4 = PWM_DAT2
@ Main Loop ----------------------------
MOV R3, #0
MOV R4, #255
_loop:
CMP R3, #255 @ Reset Counters When They
MOVGT R3, #0 @ Go Beyond Range 0..255
CMP R4, #0
MOVLT R4, #255
STR R3, [R5] @ Set DAT1 to a number between 0..255
MOV R0, #10
BL _wait @ Wait 10ms
STR R4, [R6] @ Set DAT2 to a number between 0..255
MOV R0, #10
BL _wait @ Wait 10ms
ADD R3, R3, #1 @ R3++
SUB R4, R4, #1 @ R4--
_reloop:
BAL _loop
@===============================
@ _wait
@===============================
@ Purpose:
@ Takes an initial time from
@ the TIMER, and continuously
@ checks if the amount of time
@ in R0 has elapsed.
@
@ Initial:
@ R0 = delay in ms
@
@ Registers Used:
@ R4-R7
@===============================
_wait:
_wait_prolog:
PUSH {R4-R7, LR}
MOV R6, #1000
MUL R4, R0, R6 @ R4 = desired delay (x1000 ms to us)
LDR R5, =0x3F003004 @ R5 = TIMER
LDR R6, [R5] @ initial time (us)
_wait_loop:
LDR R7, [R5] @ current time (us)
SUB R7, R7, R6 @ initial time - current time = time delayed
CMP R7, R4 @ if the desired delay has not been reached
BLT _wait_loop @ then loop again
_wait_epilog:
POP {R4-R7, PC}
Я ожидаю, что светодиод хотя бы включится и смодулирует часы, но он даже не включается.
Я уже тестировал GPIO для функциональности вывода и ввода. Используя те же аппаратные адреса, которые я использую в коде, я смог зажечь светодиод, а также заставить мигать еще один светодиод 3 раза после нажатия кнопки, поэтому функция GPIO должна работать нормально.
Единственное, что я не могу проверить так легко, это если я делаю правильные шаги для включения ШИМ или правильно настраиваю часы, чтобы они не выходили из строя.