Какие шаги необходимо выполнить для настройки ширины импульса на Raspberry Pi 3 с помощью рычага в сборе? - PullRequest
2 голосов
/ 05 мая 2019

Я работаю над голым металлическим заданием Raspberry Pi 3 (RPi3) для школы и пытаюсь получить код сборки, чтобы изменить яркость каждого диода в светодиоде RGB.

Проще говоря, я пытаюсь заставить широтно-импульсную модуляцию (ШИМ) работать на моем RPi3 с использованием ассемблерного кода и без библиотек. После исследования (подробности ниже), я думаю, я собрал все шаги, необходимые для настройки ШИМ для использования на GPIO 18 и 19. Но, используя мой код, светодиод даже не горит. Есть ли какие-то шаги, которые я пропускаю или что-то не вижу в моем коде?


Принятые шаги:

  • Я упростил свой код до процедуры сверху вниз, за ​​исключением одной подпрограмма _wait, которая проверена на работоспособность. я хотел устраните как можно больше потенциальных проблем, таких как неуместные указатели стека.
  • Я имею в виду периферийные устройства BCM2835 {1}.
  • Я протестировал ШИМ на своем Pi, используя Ultibo {2} и их пример с ШИМ с положительными результатами. Я даже прочитал / проследил их исходный код, чтобы собрать список процедур и аппаратных адресов, которые они используют. Вот где я придумал мой список, который я использую в коде сборки:

    1. Установить RNG1 и RNG2
    2. Установите РЕЖИМ1 и РЕЖИМ2
    3. Включить каналы M / S
    4. Установить частоту:
      • Остановить часы ШИМ {a}
      • Установка частоты в регистре PWM CLK DIV {b}
      • Установить часы на генератор
      • Запуск PWM Clock
    5. Установить GPIO 18 и 19 в качестве входа (это лишнее и ненужное)
    6. Установите GPIO 18 и 19 на альтернативную функцию 5 (переключает их на выходы ШИМ вместо входов)
    7. Запустить запись в бесконечный цикл в 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 должна работать нормально.

Единственное, что я не могу проверить так легко, это если я делаю правильные шаги для включения ШИМ или правильно настраиваю часы, чтобы они не выходили из строя.

...