This week we learned about embedded programming. Unlike fancy abtract computer programming using languages like Python or Ruby, the embedded world is a throwback to simpler (funner?) times: direct hardware register manipulation, clock cycle counting, interrupt priorities, datasheet peripheral chapters, and complicated toolchains. It's all worth it when the board you design and solder together starts blinking its LEDs when you tell it to, or at least that's what I tell myself when I'm trying to get a fussy peripheral to work properly. This week our group assignment was to compare the performance and development workflow of a few different architectures; our individual assignment was to (a) read a microcontroller datasheet, and (b) program our boards from circuits week to do something, using as many different languages and environments as possible.
My current research at CBA has landed on the Microchip ATSAMD51J20A as the platform of choice. This chip has a lot going for it: a floating-point unit, a fast clock, lots of SRAM, and a relatively straightforward development workflow. Most importantly, it's available and stocked in single-piece quantities in a wafer-level chip scale package (WLCSP), so it's ideal for projects where miniaturization is paramount. My colleague Jake also uses the SAMD51 for his research, so we can share best practices and give each other coding tips.
The SAMD51 and the ATTiny44A used in the class board, both shown above, are dramatically different processors, often differing in specifications by an order of magnitude or more. Here is a table comparing the two devices along a few interesting axes:
characteristic
ATtiny44A, SOIC-14
ATSAMD51J20A, WLCSP-64
architecture
AVR
ARM Cortex M4F
max clock speed, MHz
20
120
package size w/pins, mm^2
51.6
12.6
SRAM, bytes
256
262144
general purpose I/O pins (GPIOs)
12
51
input voltage range, V
1.8 - 5.5
1.7 - 3.3
bit width
8
32
datasheet weight if printed on 20-lb paper, lbs
2.3
21.3
cost ea., qty 100, USD
0.64
4.08
Toolchains for each device also vary dramatically. I tend to shy away from vendor-supplied IDEs, such as Atmel Studio (which can happily develop, compile, and flash both platforms). Instead, I try to use open-source tools using the Linux command line. This strategy often results in a steeper initial learning curve, but gives me a good deal of flexibility as I dig deeper into a project (and can make jumping to other architectures a bit easier). Another comparison table:
The last comment about ARM toolchains being a day-ruiner is a bit tongue-in-cheek, but it can be true depending on your experience with Linux dependencies. This phenomenon carries across the development cycle as well; ARM chips are so much more compicated than AVRs that seemingly simple tasks can absorb an entire evening (cue ominous foreshadowing music).
the interefoozal dedesler
First, credit to Faruk for sharing his delightful project name generator:
I have a decent bit of experience with the ATtiny44A and previously wrote a bit of code to control an RGB LED on my class board, so I decided to spin a new design using the SAMD51 to gain some experience on the platform. This also gave me an opportunity to use one of Jake's moduleboards which I am told will play a key part in Machine Week later this semester. The circuit is pretty simple, and is supposed to do two things: (a) use an on-board Hall effect sensor to sense ambient magnetic fields and display the field strength as a bar graph on a dozen LEDs; and (b) use persistence-of-vision to turn the LEDs into a text display, with the Hall effect sensor acting as a zero point to keep the text syncronized. Here is the schematic:
... and the PCB layout:
Jake's module is super cool; it breaks out all the SAMD51 pins. I only used the pins along the edge castellations; the bottom of the PCB also has a bunch of pads that can be reflow soldered for access. The board also includes a few holes so the Tag Connect programming cable sits down properly.
As usual, I milled the board using mods and the SRM-20. The work produced a suspicious amount of dust:
I encountered a few milling defects. First, the 1/64" end mill plunged too deep, despite a single 0.004" pass and proper zeroing. I ran a few iterations of the board and ended up pulling the Z offset up a few tenths of a millimeter; this workaround produced a reasonable result but required a lot of trial and error. Second, the tool path missed a few tiny bridges, highlighted here with red circles:
I cut these away with a razor. Erik suggested that I should try increasing my tolerances a bit; I'm running KiCad with 0.4mm spaces and traces, which is pretty much the limit for a 1/64" (0.39 mm) end mill. I'll try opening this up to 0.5 mm next time. After deburring, soldering down the moduleboard was simple and satisfying; I used plenty of flux, so good ventilation was key:
[command: ffmpeg -i VID_20191021_145548.mp4 -filter:v "scale=800:-1" -ss 00:00:23.0 -t 00:00:11.0 -c:a copy out.mp4]
I used the SAMD51 development environment I'd set up earlier this month; for more information on this, check out the 'bare metal' section from the CBA Hello World page on this chip. Then I spent... an inordinate amount of time trying to get the ADC to work. I verified that the Hall effect sensor was spitting out 1.0 - 2.5 VDC through its resistor divider when I waved a rare earth magnet nearby, but I wasn't able to get the microcontroller to register a value. Here is a snippet of ADC setup code I ended up with:
PORT->Group[0].PINCFG[4].bit.PMUXEN = 1; // enable input multiplexer
PORT->Group[0].PMUX[4].bit.PMUXE = 1;
REG_MCLK_APBDMASK |= (1<<7); // enables ADC0 APB clock (should be on by default)
REG_GCLK_PCHCTRL40 |= (1<<6); // enables ADC0 generic clock generator
REG_GCLK_PCHCTRL40 |= 0x01; // sets ADC0 to generic clock generator 1
REG_ADC0_INPUTCTRL |= 0x04; // set input address to AIN4 (PA4)
REG_ADC0_CTRLB |= (1<<1); // turn on free-running mode
REG_ADC0_REFCTRL |= 0x03; // set reference to VDDANA
ADC0->INPUTCTRL.reg = ADC_INPUTCTRL_MUXNEG_GND | ADC_INPUTCTRL_MUXPOS_AIN4;
ADC0->CTRLB.reg = ADC_CTRLB_RESSEL_12BIT; // set to 12-bit acquisition mode
REG_ADC0_CTRLA |= (1<<1); // set the ENABLE bit to start the ADC
I also tried a number of example code snippets I found online. My suspicion is that something weird is going on with the SAMD51's clock; in many cases, the code I found online included a blocking statement that would wait until the ADC reset or enable bit flipped to ensure the peripheral turned on properly. When I included these lines and stopped gdb after a bit of time, I found that my program was hanging while it waited for the bits to flip. More investigation is clearly needed, but I ran out of time for this week.
On the plus side, I was able to get the LEDs to work! They're 0805 pink devices I bought from a stall in Huaqiangbei, Shenzhen earlier this year; the reel of 3000 was around $15 USD, so I try to use them for everything. They are rather bright with 120 ohm resistors at 3.3 VDC:
I think there is an issue with the updateLEDs() function; here is a video of a simple binary counter, showing a few bits that aren't changing and are probably in the wrong position:
[command: ffmpeg -i VID_20191021_145548.mp4 -filter:v "scale=800:-1" -ss 00:00:23.0 -t 00:00:11.0 -c:a copy out.mp4]
... and, we're outta time. Perhaps this project will get dusted off in the future and properly finished. In any case, I'm glad I got a chance to putz about with the SAMD51. Future Zach, please fix my silly mistakes.