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/29 17:52] – [Hardware reference] pczekalski | en:multiasm:exercisesbook:avr:sut [2026/05/04 14:50] (current) – [Visualising Instruction Execution Time Using an Oscilloscope] pczekalski | ||
|---|---|---|---|
| Line 11: | Line 11: | ||
| <figure sutavrlabimage1> | <figure sutavrlabimage1> | ||
| - | {{ : | + | {{: |
| < | < | ||
| </ | </ | ||
| <figure sutavrlabimage1_2> | <figure sutavrlabimage1_2> | ||
| - | {{ : | + | {{: |
| < | < | ||
| </ | </ | ||
| 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> | ||
| The way the display works is similar to a typical matrix dot display: instead of having to control 32 independent digital lines to control each LED composing the display independently (8 per digit, 4 digits), we use a digit selector (lines 0,1,2,3) and common symbol lines (lines DP, | The way the display works is similar to a typical matrix dot display: instead of having to control 32 independent digital lines to control each LED composing the display independently (8 per digit, 4 digits), we use a digit selector (lines 0,1,2,3) and common symbol lines (lines DP, | ||
| This way, the display " | This way, the display " | ||
| - | The schematic in Figure {{ref> | + | The schematic in Figure {{ref> |
| 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, | ||
| - | Note: those registers 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. | ||
| + | ** 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 146: | Line 147: | ||
| </ | </ | ||
| + | <note important> | ||
| + | <note tip> | ||
| + | ** Display single digit: how to use it to display a number? **\\ | ||
| + | Sample code that uses the function declared above and displays 1975 is presented below. Note, the MCU runs here at full speed, constantly updating the display. While it is not necessary to (a minimum, comfortable LED display refresh rate should be around 10Hz), we do not present such a solution here for the sake of simplicity. It is common to address timers for this job to periodically refresh the screen. | ||
| + | <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 | ||
| + | |||
| + | ; 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 184: | 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> | ||
| + | {{: | ||
| + | < | ||
| + | </ | ||