-
The illusion of diffusion
10/08/2024 at 22:58 • 2 commentsSince the SK6805 RGB LEDs are side-firing, they don't emit much light through the PCB, leaving a lackluster showing.
My first attempt at diffusion was to laser-cut some clear acrylic in the outlines of the shapes to act as a kind of light pipe in the hope that helped. The result was barely imperceptible.
I roughed up the back of the acrylic in the hope that would bounce more light through the front, but the majority of the light still was strongest at the edges.
Next I tried 3D printing a frame and adding reflective material to the back to help bounce the light through. This worked okay, but logistically it's a bunch of extra fiddly work and I still hadn't come up with a good way of fixing it to the PCB.
Finally I tried some hot glue, and it made the biggest difference for the least amount of work. It's a bit messy, but it works, and it's on the back where no-one is looking :)
-
Reverse Indexing
10/08/2024 at 22:24 • 0 commentsDue to the pinout/footprint of the SK6805 RGB LEDs, it was more beneficial to arrange them so that the final ghost was LED 0, and Pac-Man was LED 4. This way I was able to connect DOUT to the next LED's DIN with a direct line.
This ended up being a tradeoff between software gymnastics to mirror the indexes, or having a far more difficult to route PCB layout, particularly with all of that empty space where I can't have copper or soldermask because I want only the FR4 exposed to allow the light through from the back.
Just remembering to mirror the indexes proved pretty straightforward and in some case you can use easier helpers e.g.#define PAC_MAN_LED_INDEX 4
-
Perfecting the pinout
10/07/2024 at 04:44 • 0 commentsNow, here's the challenge:
The micro has only 8 pins, and the SAO connector has 6 and there are limitations to the configuration of the pins, and the functions they support, so the right choices here are critical.
I need the following:
- VCC & GND
- A regular GPIO for pushbutton input
- SDA and SDC for I2C input
- SPI MOSI to drive the RGB LEDs
- Retain the SWDIO and SWCLK for programming/debugging.
This took equal parts planning and experimentation, with one layout revision and re-routing the board, but I was able to make it work. The key to this success was changing Pin 6 NRST function to a regular GPIO, and this is achieved by setting some user options in the flash. Again, I relied on a HAL example to get this to work, and the SAO will check the mode on boot up and set the flash if it needs to then reboot - this is completely reversible by changing a define in the code and recompiling, so I didn't brick any of my $0.12 chips :)
This is the magic bit of code that makes it possible and the first thing that happens in the main() function.
HAL_Init(); /* Check RST pin mode */ #if defined(RSTPIN_MODE_GPIO) if( READ_BIT(FLASH->OPTR, FLASH_OPTR_NRST_MODE) == OB_RESET_MODE_RESET) #else if( READ_BIT(FLASH->OPTR, FLASH_OPTR_NRST_MODE) == OB_RESET_MODE_GPIO) #endif { // program option byte HAL_FLASH_Unlock(); /* Unlock FLASH */ HAL_FLASH_OB_Unlock(); /* Unlock OPTION */ FLASH_OBProgramInitTypeDef OBInitCfg = {0}; /* Configure OPTION settings */ OBInitCfg.OptionType = OPTIONBYTE_USER; OBInitCfg.USERType = OB_USER_BOR_EN | OB_USER_BOR_LEV | OB_USER_IWDG_SW | OB_USER_NRST_MODE | OB_USER_nBOOT1; #if defined(RSTPIN_MODE_GPIO) /* Disable BOR/Enable BOR rising 3.2V, falling 3.1V/Software watchdog mode/GPIO functionality/System memory as boot area */ OBInitCfg.USERConfig = OB_BOR_DISABLE | OB_BOR_LEVEL_3p1_3p2 | OB_IWDG_SW | OB_RESET_MODE_GPIO | OB_BOOT1_SYSTEM; #else /* Disable BOR/Enable BOR rising 3.2V, falling 3.1V/Software watchdog mode/RST functionality/System memory as boot area */ OBInitCfg.USERConfig = OB_BOR_DISABLE | OB_BOR_LEVEL_3p1_3p2 | OB_IWDG_SW | OB_RESET_MODE_RESET | OB_BOOT1_SYSTEM; #endif /* Start option byte programming */ HAL_FLASH_OBProgram(&OBInitCfg); HAL_FLASH_Lock(); /* Lock FLASH */ HAL_FLASH_OB_Lock(); /* Lock OPTION */ /* Generate a reset to load the option bytes */ HAL_FLASH_OB_Launch(); }
This means I can then go on to use Pin 6 (PA2) in it's alternate function mode as I2C SDA.
The final pin configuration I used is as follows.
Pin Name Function SAO Header 1 Vcc Power in VCC 2 PA4/PA10 I2C SCL (alternate function) SCL 3 PA3 SPI MOSI (LED driver) N/A 4 PA14-SWC/PB3 SWCLK (for programming/debugging) GPIO2 5 PA13-SWD SWDIO GPIO1 6 PA2/PF2 I2C SDA SDA 7 PA1 GPIO pushbutton input N/A 8 Vss Ground GND This configuration theoretically means you could make a SWD compatiple programmer from your badge and re-program the SAO from it.
-
Low-level vs Hardware Absraction Layer
10/07/2024 at 03:19 • 0 commentsThe next struggle I faced was getting certain things to work while poking at the hardware directly using the low-level registers.
I studied the comprehensive datasheet and reference manual thinking I had a good handle on everything. While GPIO seemed to work just as expected, I had issues with getting I2C working properly, and also could not get flash working either.
I had avoided the Hardware Abstraction Layers (HAL Drivers) thus far, fearing the bloated flexibility would eat up my precious memory and not leave enough for what I wanted.
After many days of struggle, I tried a few HAL examples, and was surprised that they worked just as intended, so I ended up copying the HAL sample projects and retrofitting my code into the example's shell.
Bottom line is that using the HAL saves a bunch of time and frustration, and is also more intuitive to program, e.g.
The low-level way to set up GPIO
GPIOA->MODER = (GPIOA->MODER & ~((uint32_t)0b11<<(1<<1))); GPIOA->PUPDR = (GPIOA->PUPDR & ~((uint32_t)0b11<<(1<<1))) | ((uint32_t)0b01<<(1<<1));
The HAL way (more verbose, but much nicer)
GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
The HAL helpers and defines are much easier to work with and make your code far more readable so you can ignore the complexities of the hardware quirks and focus on the more meaningful magic of your project.
For the curious, you can step through the HAL code in the debugger and see what it's doing at the hardware level. I was surprised to see that it didn't always follow the datasheet 1:1 which some unexplainable bit setting here and there. At least I don't need to worry too much about it now.Another thing I found out far too late - Since this chip is based on an ARM Cortex M0+ (possibly not licensed though), you can search from problems using "STM32" or "Core M0+" when you get stuck on things and the solutions may actually work, or at least get you closer to the answer.
-
Getting Set up
10/07/2024 at 03:01 • 0 commentsThe Puya PY32F002A at the time of writing is not as widely supported as many other micro-controllers, but it is really cheap - ~$0.12 each when you buy 100 qty.
Getting a dev environment up was a challenge and it took a frustrating amount of time to find something workable, with many missteps along the way.
What I ultimately landed on (and recommend), is using ARM's Keil MDK with uVision which can be downloaded for just the cost of your email here.
Once you have that, get the latest Puya firmware from their website. At the time of writing, that's version 1.41 and can be found here.
The SDK includes several projects that are already set up for uVision, and I recommend using them as a starting point.
The last thing you'll need is a programmer - You can't (easily) program the PY32F002A over UART like you're used to, so you need a programmer compatible with ARM's Serial Wire Debug (SWD) protocol. I ended up with 3 different programmers, but to be honest on of the cheap ST-Link v2 clones works just fine. The bonus for jumping through this particular hoop is that you will have breakpoints and step by step debugging - absolute luxury!