This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| en:multiasm:exercisesbook:avr:sut [2026/04/30 09:39] – [Hardware reference] pczekalski | en:multiasm:exercisesbook:avr:sut [2026/05/04 14:50] (current) – [Visualising Instruction Execution Time Using an Oscilloscope] pczekalski | ||
|---|---|---|---|
| Line 30: | Line 30: | ||
| </ | </ | ||
| + | |||
| + | ==== Handling of the buffered 4-digit, 7-segment display ==== | ||
| To display a digit in the 4x7seg. display, there are two definitions needed: the shape of a digit (or other symbol), and its position (1,2,3,4: a binary mask). | To display a digit in the 4x7seg. display, there are two definitions needed: the shape of a digit (or other symbol), and its position (1,2,3,4: a binary mask). | ||
| Line 40: | Line 42: | ||
| .byte 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90 | .byte 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90 | ||
| </ | </ | ||
| + | In a common-anode configuration, | ||
| + | |||
| <note tip> | <note tip> | ||
| Line 47: | Line 51: | ||
| When binary combinations in both registers (line and symbol) are ready to be represented, | When binary combinations in both registers (line and symbol) are ready to be represented, | ||
| + | ** Display single digit: function definition **\\ | ||
| To handle display, a sample function that displays a digit in a selected position is presented below. Note that it does not check parameters and thus assumes that the digit position is a number between 0 and 3, and that a digit to display is 0..9. Going beyond these limits causes unpredictable behaviour and usually an MCU program crash. | To handle display, a sample function that displays a digit in a selected position is presented below. Note that it does not check parameters and thus assumes that the digit position is a number between 0 and 3, and that a digit to display is 0..9. Going beyond these limits causes unpredictable behaviour and usually an MCU program crash. | ||
| <code asm> | <code asm> | ||
| Line 142: | Line 147: | ||
| </ | </ | ||
| - | Note: registers in this schema store data for only ONE digit. Iterating over digits and displaying them allows it to represent a full, multi-digit number. To display, e.g., 1023, it is necessary to handle each digit separately: " | + | <note important> |
| + | <note tip> | ||
| - | Sample code that uses the function declared above and displays | + | ** Display single digit: how to use it to display a number? **\\ |
| + | Sample code that uses the function declared above and displays | ||
| <code asm> | <code asm> | ||
| + | .equ SREG, | ||
| + | .equ SPH, 0x3E ; Stack Pointer High | ||
| + | .equ SPL, 0x3D ; Stack Pointer Low | ||
| + | .equ SER_PORT, 0x05 ; PORTB I/O address | ||
| + | .equ PINB, | ||
| + | .equ SER_PIN, | ||
| + | .equ DDRD, | ||
| + | .equ DDRB, | ||
| + | .equ CLK_PORT, 0x0B ; PORTD I/O address | ||
| + | .equ CLK_PIN, | ||
| + | .equ LAT_PORT, 0x0B ; PORTD I/O address | ||
| + | .equ LAT_PIN, | ||
| + | .equ RAMEND, | ||
| + | .global display_digit | ||
| + | ; --------------------------------------------------------- | ||
| + | ; Data stored in Program Memory (Flash) | ||
| + | ; --------------------------------------------------------- | ||
| + | .section .text | ||
| + | .org 0x0000 | ||
| + | rjmp RESET | ||
| + | |||
| + | |||
| + | RESET: | ||
| + | |||
| + | ; Prepare stack | ||
| + | ldi r16, hi8(RAMEND) | ||
| + | out SPH, r16 | ||
| + | ldi r16, lo8(RAMEND) | ||
| + | out SPL, r16 | ||
| + | ; Initialise display control outputs | ||
| + | sbi DDRB, SER_PIN | ||
| + | sbi DDRD, CLK_PIN | ||
| + | sbi DDRD, LAT_PIN | ||
| + | |||
| + | clr r25 | ||
| + | clr r23 | ||
| + | ; --- Main Loop, displays in sequence 1-> | ||
| + | LOOP: | ||
| + | ldi r24,0 | ||
| + | ldi r22,1 | ||
| + | call display_digit ; Display 1 | ||
| + | ldi r24,1 | ||
| + | ldi r22,9 | ||
| + | call display_digit ; Display 9 | ||
| + | ldi r24,2 | ||
| + | ldi r22,7 | ||
| + | call display_digit ; Display 7 | ||
| + | ldi r24,3 | ||
| + | ldi r22,5 | ||
| + | call display_digit ; Display 5 | ||
| + | rjmp LOOP | ||
| + | |||
| + | ; void display_digit(uint8_t pos, uint8_t number); | ||
| + | ; r24 = position (0 to 3) | ||
| + | ; r22 = number (0 to 9) | ||
| + | |||
| + | .... here comes the body of the display_digit function | ||
| </ | </ | ||
| + | In the function above, we used fixed (constant) digits to display. A common scenario, however, is when the number is stored in some register or in a memory variable. | ||
| + | ** Convert number to digits: function definition **\\ | ||
| + | To display a number on this kind of display, you need to convert it into an array of bytes, each representing a digit. A function below does the trick. | ||
| + | <code asm> | ||
| + | ; void convert_to_digits(uint16_t value, uint8_t* array); | ||
| + | ; Inputs: | ||
| + | ; r25:r24 = Value to convert (up to 9999) | ||
| + | ; r23:r22 = Pointer to SRAM array (4 bytes long) | ||
| + | convert_to_digits: | ||
| + | ; Save registers we are about to use | ||
| + | push r26 | ||
| + | push r27 | ||
| + | push r18 | ||
| + | push r19 | ||
| + | push r20 | ||
| - | To display a number, you need to convert it to an array of bytes, each representing a digit. A function below does the trick. | + | ; Move the SRAM pointer from r23:r22 into the X pointer (r27:r26) |
| + | movw r26, r22 | ||
| + | ; --------------------------------------------------- | ||
| + | ; 1. Thousands Digit (Subtract 1000 = 0x03E8) | ||
| + | ; --------------------------------------------------- | ||
| + | clr r18 ; Clear digit counter | ||
| + | ldi r19, 0x03 ; High byte of 1000 | ||
| + | ldi r20, 0xE8 ; Low byte of 1000 | ||
| + | loop_1000: | ||
| + | cp r24, r20 ; Compare value low byte with 1000 low byte | ||
| + | cpc r25, r19 ; Compare value high byte with 1000 high byte | ||
| + | brlo done_1000 | ||
| + | sub r24, r20 ; Subtract 1000 low byte | ||
| + | sbc r25, r19 ; Subtract 1000 high byte (with carry) | ||
| + | inc r18 ; Increment thousands digit | ||
| + | rjmp loop_1000 | ||
| + | done_1000: | ||
| + | st X+, r18 ; Store thousands digit in array[0] and increment X | ||
| + | |||
| + | ; --------------------------------------------------- | ||
| + | ; 2. Hundreds Digit (Subtract 100 = 0x0064) | ||
| + | ; --------------------------------------------------- | ||
| + | clr r18 ; Reset digit counter | ||
| + | ldi r19, 0x00 ; High byte of 100 | ||
| + | ldi r20, 0x64 ; Low byte of 100 | ||
| + | loop_100: | ||
| + | cp r24, r20 | ||
| + | cpc r25, r19 | ||
| + | brlo done_100 | ||
| + | sub r24, r20 | ||
| + | sbc r25, r19 | ||
| + | inc r18 | ||
| + | rjmp loop_100 | ||
| + | done_100: | ||
| + | st X+, r18 ; Store hundreds digit in array[1] and increment X | ||
| + | |||
| + | ; --------------------------------------------------- | ||
| + | ; 3. Tens Digit (Subtract 10 = 0x000A) | ||
| + | ; --------------------------------------------------- | ||
| + | clr r18 ; Reset digit counter | ||
| + | ldi r19, 0x00 ; High byte of 10 | ||
| + | ldi r20, 0x0A ; Low byte of 10 | ||
| + | loop_10: | ||
| + | cp r24, r20 | ||
| + | cpc r25, r19 | ||
| + | brlo done_10 | ||
| + | sub r24, r20 | ||
| + | sbc r25, r19 | ||
| + | inc r18 | ||
| + | rjmp loop_10 | ||
| + | done_10: | ||
| + | st X+, r18 ; Store tens digit in array[2] and increment X | ||
| + | |||
| + | ; --------------------------------------------------- | ||
| + | ; 4. Ones Digit (The Remainder) | ||
| + | ; --------------------------------------------------- | ||
| + | ; Whatever is left in r24 is the ones digit (0-9) | ||
| + | st X, r24 ; Store ones digit in array[3] (no need to increment X) | ||
| + | |||
| + | ; Restore registers and return | ||
| + | pop r20 | ||
| + | pop r19 | ||
| + | pop r18 | ||
| + | pop r27 | ||
| + | pop r26 | ||
| + | ret | ||
| + | </ | ||
| + | Note, this function operates on a buffer located in the memory, which can be declared, e.g. as follows: | ||
| + | <code asm> | ||
| + | .section .bss ; .bss is for uninitialized variables in SRAM | ||
| + | ; Reserve 4 bytes in SRAM to hold the 4 converted digits | ||
| + | display_array: | ||
| + | .space 4 | ||
| + | </ | ||
| ===== Communication ===== | ===== Communication ===== | ||
| Devices (laboratory nodes) are interconnected in pairs, so it is possible to work in groups and implement scenarios involving more than one device: | Devices (laboratory nodes) are interconnected in pairs, so it is possible to work in groups and implement scenarios involving more than one device: | ||
| Line 188: | Line 340: | ||
| <note tip> | <note tip> | ||
| + | |||
| + | ==== Visualising Instruction Execution Time Using an Oscilloscope ==== | ||
| + | Let's try to visualise how code operates the GPIO. Naturally, in the remote lab, it is not possible to do it remotely, so here we present some desk-based experiments.\\ | ||
| + | The '' | ||
| + | In the function that displays a single digit, there is a section that loads a binary mask into the internal registers, enabling the LED segments that constitute the digit to be turned on and off. It is: | ||
| + | <code asm> | ||
| + | ... | ||
| + | sbi LAT_PORT, LAT_PIN | ||
| + | cbi LAT_PORT, LAT_PIN | ||
| + | ... | ||
| + | </ | ||
| + | |||
| + | The figures {{ref> | ||
| + | '' | ||
| + | The Arduino Uno operates at 16 MHz, so each cycle is 1/16000000 s, which is about 63 ns. According to the documentation, | ||
| + | |||
| + | <figure arduinounodigitoscilloscope1> | ||
| + | {{: | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | <figure arduinounodigitoscilloscope2> | ||
| + | {{: | ||
| + | < | ||
| + | </ | ||