Skip to content

9. Embedded programming

Introduction

Utilizing other environments to upload code to Arduino and other microcontroller boards was an entirely foreign concept to me prior to the ninth week of Fab Academy. This week’s introduction to embedded programming introduced me to an entirely new world of alternate environments and languages that can be used to send instructions to a microcontroller board. Ultimately, the softwares that I learned how to use this week and the preponderance of useful tricks that I acquired will prove incredibly useful throughout the remainder of Fab Academy as well as the many instances that I will be required to upload code to a variety of different microcontrollers throughout my life.

Assignment

  • group assignment: compare the performance and development workflows for other architectures.
  • individual assignment: read a microcontroller data sheet, program your board to do something with as many different programming languages and programming environments as possible.
  • Week 9 Individual Assignment

    This week’s individual assignment was to read a microcontroller data sheet and to program my board that I designed, soldered, and programmed during week 7 of my FabAcademy experience. The board that I designed in week 7 performs the same function as all of my classmates. It includes an ATtiny412 microcontroller, a button, and an LED, with 6 FTDI header pins that allow users to program the board via an Arduino or a custom programmer like the ones that we made during week 5.

    Reading a micrcontroller data sheet

    As the first part of this week’s assignment was to read a microcontroller data sheet, I located the datasheet for the board that I would be using, the ATTiny412, by using the following link. After finding the datasheet, I began searching for the individual port registers for each of the 8 pins on the microcontroller, which would allow me to write to each pin using its unique port register rather than using the Arduino ‘digitalWrite’ function that performs a variety of background tasks that are not required for successful operation of a microcontroller. Simply writing to a port register is significantly faster than using 'digitalWrite' as assigning values to port registers does not consume extra memory and simply changes a binary state of a pin. Here is an article with some extra information regarding the speed of digitalWrite compared to that of simply writing to port registers. Peter goes into extraordinary detail by using an oscilloscope to display the celerity of writing to port registers compared to simply using digitalWrite. While the application of register-level coding is not required on a board like mine that does not need to capitlize on the extra speed of register-level programming, this knowledge is certainly vital when writing programs that parse through and interpret large data sets in short amounts of time. After reading this article, I discovered the massive difference in speed between 'digitalWrite' and register-level programming by analyzing the oscilloscope pictures that display the frequency disparities between the two.

    ARDUINO digitalWrite function:
     258: 0f 93        push r16
     25a: 1f 93        push r17
     25c: cf 93        push r28
     25e: df 93        push r29
     260: 1f 92        push r1
     262: cd b7        in r28, 0x3d ; 61
     264: de b7        in r29, 0x3e ; 62
     266: 28 2f        mov r18, r24
     268: 30 e0        ldi r19, 0x00 ; 0
     26a: f9 01        movw r30, r18
     26c: e8 59        subi r30, 0x98 ; 152
     26e: ff 4f        sbci r31, 0xFF ; 255
     270: 84 91        lpm r24, Z
     272: f9 01        movw r30, r18
     274: e4 58        subi r30, 0x84 ; 132
     276: ff 4f        sbci r31, 0xFF ; 255
     278: 14 91        lpm r17, Z
     27a: f9 01        movw r30, r18
     27c: e0 57        subi r30, 0x70 ; 112
     27e: ff 4f        sbci r31, 0xFF ; 255
     280: 04 91        lpm r16, Z
     282: 00 23        and r16, r16
     284: c9 f0        breq .+50      ; 0x2b8 
     286: 88 23        and r24, r24
     288: 21 f0        breq .+8       ; 0x292 <digitalWrite+0x3a>
     28a: 69 83        std Y+1, r22 ; 0x01
     28c: 0e 94 ca 00  call 0x194 ; 0x194 
     290: 69 81        ldd r22, Y+1 ; 0x01
     292: e0 2f        mov r30, r16
     294: f0 e0        ldi r31, 0x00 ; 0
     296: ee 0f        add r30, r30
     298: ff 1f        adc r31, r31
     29a: ec 55        subi r30, 0x5C ; 92
     29c: ff 4f        sbci r31, 0xFF ; 255
     29e: a5 91        lpm r26, Z+
     2a0: b4 91        lpm r27, Z
     2a2: 9f b7        in r25, 0x3f ; 63
     2a4: f8 94        cli
     2a6: 8c 91        ld r24, X
     2a8: 61 11        cpse r22, r1
     2aa: 03 c0        rjmp .+6       ; 0x2b2 <digitalWrite+0x5a>
     2ac: 10 95        com r17
     2ae: 81 23        and r24, r17
     2b0: 01 c0        rjmp .+2       ; 0x2b4 <digitalWrite+0x5c>
     2b2: 81 2b        or r24, r17
     2b4: 8c 93        st X, r24
     2b6: 9f bf        out 0x3f, r25 ; 63
     2b8: 0f 90        pop r0
     2ba: df 91        pop r29
     2bc: cf 91        pop r28
     2be: 1f 91        pop r17
     2c0: 0f 91        pop r16
     2c2: 08 95        ret
    }
    

    Finally, the article provided the entire 'digitalWrite' function that exists within the Arduino IDE. The above code displays the many background tasks that 'digitalWrite' performs that contribute to its comparatively slow speed to simply programming at the board register level. While programming using port registers is significantly faster, it does eliminate the use of the many tests that are performed by the 'digitalWrite' function, which could potentially prove useful when debugging, especially if a device does not require the added speed of register-level programming, which is its only true advantage on paper.

    pg1

    On the ATTiny412 datasheet, I located the section that described the pinout on the thirteenth page of the document. As I already knew the location of the sixth, second, ground, and power pins, I only really needed to use this pinout to find the individual port registers of each pin on the next page, especially since I was not going to be using any additional pins on my button board than I had already used in previous weeks.

    In order to learn how to actually manipulate the pins on the board, I needed to familiarize myself with the process of programming at the port register level. This necessitated reading a multitude of articles and consuming a plethora of media regarding this process that is incredibly complex even when I only need to tell two pins what to do. This is the first video that I watched that explained how to use an ATMega328P microcontroller by writing to the port registers. While I would be using the ATtiny412 microcontroller on my board, this video provided an incredibly useful explanation of how to actually interact with port registers, which is a universal process that works on every microcontroller. This video also showed me how to write a ‘blink’ example sketch by refraining from utilizing the ‘digitalWrite’ function and instead writing to the port registers themselves. I learned that ‘PB’ in a pin reference means ‘PORTB’ and the number after it references which bit of the 8-bit architecture of a microcontroller the pin belongs to. For example, ‘PB3’ would mean ‘Port B, bit 3.’ To use this knowledge, it is important to look at the actual number that is beside the ‘PB#’ in order to know which actual number you are writing to. This video further demonstrates the superiority of programming closer to metal by writing to port registers by displaying that the fastest on/off speed possible for the guy in the video was a frequency of 278 mHz, while the frequency of using digitalWrite is generally capped around 148kHz. Obviously, this difference is incredibly large. (2,780,000 MHz vs 148,000 Hz) Next, the video explained how to read the DDRB section on the datsheet in order to manipulate the ‘pinmode’ settings on the Arduino.

    pg1

    After familiarizing myself with how to program Arduinos on the port register level, I looked at the datasheet for the ATTiny412 microcontroller again and attempted to look for the port register nomenclature that is specific to this microcontroller. I located the above information on page 14, and discovered that pins need to be referenced using a Pxn or PORTx_PINn format.

    pg1

    After continuing to read through the datasheet, I located more useful information on the eighteenth page of the document which described the process of referring to unique pins on the device. The ATTiny412 only features 3 output/input pins, so the datasheet told me that each pin can be referenced using addresses 0x00 - 0x03. I learned that each pin can be referenced as follows: I/O Pin 1 - 0x1E I/O Pin 2 - 0x92 I/o Pin 3 - 0x23 - I also discovered that the only difference between the ATTiny212 and ATTiny412 is the register for the third I/O pin, which is referred to as 0x21 on the ATTiny212 and 0x23 on the ATTiny412. pg1 After gathering the necessary information from the ATTiny412 datasheet, I was still unsure of how to refer to specific pins on the Arduino via port registries. As such, I began perusing the megaTinyCore GitHub page in search of any useful information. Eventually, I located a section of the page by navigating the path megaTinyCore / megaavr / extras / direct port manipulation. After reading through the information on this page within the MegaTinyCore GitHub repository, I learned how to reference different pins on the ATTiny412 by using port registers and how to change the state of pins by modifying their 8-bit addresses. I also learned that each pin is set to an output by default, so I would only need to initialize my button pin when programming as the LED pin would already be configured as an output. After reading through this page on the library's GitHub page, I was ready to modify the button blink code that I wrote during electronics design week by replacing every function and pin reference with bare metal alternatives.

    Programming my button board with port registers

    Before writing the code for the button, I decided to use port registers to make the LED on my board blink without requiring input from the button in order to ensure that I actually knew how to use registers to program. Writing this code was relatively simple, as I simply needed to set pin 1 as an output and set the rest of the pins on the board to their default input values. To accomplish this, I added ‘PORTA.DIR = 0b01000000’ to the setup section of my code. Next, I needed to turn the button on and off after a set delay period in order to make it blink. To accomplish this, I used information from the megaTinyCore library that described how to use ‘|=’ and ‘&=∼’ to swap outputs between their high and low states without requiring the use of the ‘digitalWrite’ function that is generally utilized to perform this task. After adding off and on states and delays to the loop function of my code, I was left with the following code.
    const int buttonPin = 1; 
    const int ledPin = 0;    
    
    void setup() {
      PORTA.DIR = 0b01000000;
    }
    
    void loop() {
      PORTA.OUT |= 0b01000000;
      delay(1000);
      PORTA.OUT &=~0b01000000;
      delay(1000);
    }
    

    After successfully uploading my blink code to my button board, I then decided to add an input to the code, which would turn the LED off when the button on my board was pressed. Writing this code was relatively straightforward, as I had already written a program that effectively did the same thing on the same board several weeks ago just without using port registers and instead using the general Arduino functions to perform every task in the code. Nevertheless, switching any pin references to port registers was not very difficult, as I already knew which components occupied which pins on my Arduino. In order to interpret input from the button on my board, I wrote the following conditional statement that checks for the press of the button on my board.

    if(bool status = PORTA.IN & PIN1_bm){
    

    Next, I added the same code that I wrote for the LED blink code using registers to turn off the LED when a button press is detected and to turn the LED on when the button was not pressed. After adding these statements, here’s the final code that I uploaded to my button blink board:

    const int buttonPin = 1; 
    const int ledPin = 0;    
    
    void setup() {
      PORTA.DIR = 0b01000000;
    }
    
    void loop() {
      if(bool status = PORTA.IN & PIN1_bm){
        PORTA.OUT |= 0b01000000;
        delay(100);
      }
      else{
      PORTA.OUT &=~0b01000000; }
    }
    

    I added a small delay at the bottom of the conditional statement that I use for detecting a button press to extend the duration of a button press "event" and allow the effect of a press to be clearer.

    Additionally, due to the modicum of power that is offered by the ATTiny412 microcontroller and the demanding processes included in my button press code, the inclusion of a large heatsink was necessitated in order to cool the microcontroller. Had I not added a heatsink to the board, it likely would have overheated upon uploading the code.

    Programming my board with PlatformIO

    Prior to this week, all of my experience programming C-based microcontrollers was in the Arduino IDE, as it is incredibly beginner friendly and offers a vast array of libraries for more knowledgeable users, meaning that switching platforms after learning the basics of Arduino is not required. While I was aware of other development environments prior to this week's assignment, I had never considered switching to a new interface to be worthwhile, as other platforms do not offer features that would potentially pique one's interest in an alternative IDE.

    After successfully controlling my LED through the press of a button, I had maximized the capabilities of the button board that I designed in terms of the tasks that I could make it complete with just a single input and a single output. As such, I ceased my operations out of the Arduino IDE for the remainder of the week and decided to explore a sundry of alternate platforms to upload similar code to my button board in order to accomplish the same task. Ultimately, programming using port registers on my button board revealed a significantly more celeritous means of uploading code to an avr microcontroller, though I remain unaware of the quantifiable merit of utilizing port registers as an alternative of traditional Arduino functions that accomplish the same task. While tasks are performed significantly faster by using port registers on a micro scale, my exploration of bare-metal programming was largely devoid of any perceptible difference from when I uploaded a similar code during electronics design week that does precisely the same thing as the register code that I wrote during my exploration of embedded programming throughout this week.

    Group Assignment

    The group assignment for this week was to compare the performance and development workflows for other architectures. To accomplish this task we used the Raspberry Pi 4 and Raspberry Pi Pico to make an LED blink an each device. I did all of the work for this week, with photography performed by Teddy Warner.


    Last update: June 22, 2021