This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
| en:multiasm:exercisesbook:avr:sut:scenarios:avr5 [2026/05/02 10:39] – created pczekalski | en:multiasm:exercisesbook:avr:sut:scenarios:avr5 [2026/05/04 13:27] (current) – pczekalski | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== | + | ====== |
| + | |||
| + | In this scenario, you will control the LED brightness using PWM. To compare results, the built-in LED will be set to 100% on while you adjust LED4's brightness. | ||
| + | |||
| + | ** Prerequisites **\\ | ||
| + | You need to book one of the AVR laboratory nodes and ensure the video stream is live.\\ | ||
| + | Get familiar with the scenario: [[avr1|]]. | ||
| + | |||
| + | ** Scenario **\\ | ||
| + | Create an application that toggles the built-in LED (GPIO 13) on permanently, | ||
| + | |||
| + | In this example, we will build the code from the bottom up. | ||
| + | |||
| + | ** Result **\\ | ||
| + | Observe the heartbeat on LED4 (GPIO 10) and the solid LED on GPIO 13 (LED1, built-in). | ||
| + | |||
| + | ** Start **\\ | ||
| + | Mind to use AVR GCC syntax (as in the instruction): | ||
| + | |||
| + | ** Step 1 **\\ | ||
| + | Compose a template for your application that includes declarations for GPIO and a timer. We use Timer1 (16-bit) to generate PWM on GPIO 10 (channel B): | ||
| + | <code asm> | ||
| + | .equ DDRB, | ||
| + | .equ PORTB, | ||
| + | .equ SPCR, | ||
| + | .equ MCUSR, | ||
| + | .equ SPL, 0x3D ; Stack Pointer Low (I/O) | ||
| + | .equ SPH, 0x3E ; Stack Pointer High (I/O) | ||
| + | .equ WDTCSR, 0x60 ; Watchdog Timer Control Register (Memory) | ||
| + | .equ TCCR1A, 0x80 ; Timer1 Control Register A (Memory) | ||
| + | .equ TCCR1B, 0x81 ; Timer1 Control Register B (Memory) | ||
| + | .equ OCR1BL, 0x8A ; Timer1 Output Compare Register B Low | ||
| + | .equ OCR1BH, 0x8B ; Timer1 Output Compare Register B High | ||
| + | |||
| + | ; Bit Constants | ||
| + | .equ PB2, 2 ; Pin 10 | ||
| + | .equ PB5, 5 ; Pin 13 | ||
| + | .equ COM1B1, 5 | ||
| + | .equ WGM10, | ||
| + | .equ CS11, 1 | ||
| + | |||
| + | .section .text | ||
| + | |||
| + | ; ========================================================== | ||
| + | ; CLASSICAL INTERRUPT VECTOR TABLE (104 Bytes) | ||
| + | ; ========================================================== | ||
| + | .org 0x0000 | ||
| + | jmp RESET | ||
| + | |||
| + | RESET: | ||
| + | .... | ||
| + | </ | ||
| + | |||
| + | ** Step 2 **\\ | ||
| + | Initialise stack (obligatory, | ||
| + | <code asm> | ||
| + | ... | ||
| + | ldi r16, hi8(RAMEND) | ||
| + | out SPH, r16 | ||
| + | ldi r16, lo8(RAMEND) | ||
| + | out SPL, r16 | ||
| + | |||
| + | ; Setup Pins using Atomic ' | ||
| + | sbi DDRB, PB5 ; Set Pin 13 as Output | ||
| + | sbi DDRB, PB2 ; Set Pin 10 as Output | ||
| + | cbi PORTB, PB5 ; Turn ON Pin 13 | ||
| + | ... | ||
| + | </ | ||
| + | |||
| + | ** Step 3 **\\ | ||
| + | Configure timer for PWM (Timer1, channel B -> GPIO 10): | ||
| + | <code asm> | ||
| + | ... | ||
| + | ldi r16, (1 << COM1B1) | (1 << WGM10) | ||
| + | sts TCCR1A, r16 | ||
| + | ldi r16, (1 << CS11) | ||
| + | sts TCCR1B, r16 | ||
| + | ldi r16, 0 | ||
| + | sts OCR1BH, r16 | ||
| + | sts OCR1BL, r16 | ||
| + | ... | ||
| + | |||
| + | </ | ||
| + | |||
| + | ** Step 4 **\\ | ||
| + | Now, AFTER the loop section (you perhaps don't have it yet, so put a dummy one in the code, temporary, implement functions to delay and to blink. Delays based on timer tick counting, not a timer, because we use Timer1 for PWM generation. | ||
| + | There are 3 functions: | ||
| + | * Function '' | ||
| + | * Function '' | ||
| + | * Function '' | ||
| + | <code asm> | ||
| + | ; void pulse(void) | ||
| + | pulse: | ||
| + | ldi r18, 0 | ||
| + | fade_in: | ||
| + | sts OCR1BL, r18 | ||
| + | rcall delay_1ms | ||
| + | inc r18 | ||
| + | brne fade_in | ||
| + | |||
| + | ldi r18, 255 | ||
| + | fade_out: | ||
| + | sts OCR1BL, r18 | ||
| + | rcall delay_1ms | ||
| + | dec r18 | ||
| + | brne fade_out | ||
| + | |||
| + | sts OCR1BL, r18 | ||
| + | ret | ||
| + | |||
| + | ; void delay_N_ms(uint8_t ms) | ||
| + | ; Expects argument ' | ||
| + | delay_N_ms: | ||
| + | tst r24 | ||
| + | breq dn_done | ||
| + | dn_loop: | ||
| + | rcall delay_1ms | ||
| + | dec r24 | ||
| + | brne dn_loop | ||
| + | dn_done: | ||
| + | ret | ||
| + | |||
| + | ; void delay_1ms(void) | ||
| + | delay_1ms: | ||
| + | ldi r21, 21 | ||
| + | d1_loop: | ||
| + | ldi r22, 250 | ||
| + | d2_loop: | ||
| + | dec r22 | ||
| + | brne d2_loop | ||
| + | dec r21 | ||
| + | brne d1_loop | ||
| + | ret | ||
| + | </ | ||
| + | <note tip> | ||
| + | |||
| + | ** Step 5 **\\ | ||
| + | Set the main loop. Here you're on your own, but the hint for the algorithm to have a real heartbeat is as follows: | ||
| + | < | ||
| + | loop infinitely: | ||
| + | // --- First beat (Lub) --- | ||
| + | pulse() | ||
| + | delay(200) | ||
| + | |||
| + | // --- Second beat (Dub) --- | ||
| + | pulse() | ||
| + | delay(750) | ||
| + | </ | ||
| + | |||
| + | ** Result validation **\\ | ||
| + | The LED4 should be flashing at a set interval (heartbeat), | ||
| + | |||
| + | ** FAQ **\\ | ||
| + | When using the printed version of this manual, please refer to the latest online version for the most up-to-date list of FAQs.\\ | ||
| + | |||
| + | **It does not work at all**: Did you compile and upload to the device? Those are separate steps: it is not enough to just compile, but you also need to " | ||
| + | **Built-in LED is off**: Mind it is driven by zero, not by one. | ||
| + | |||
| + | |||