- 1 Overview
- 2 Hardware (Electronics)
- 3 Hardware (Enclosure)
- 4 Software
- 5 Notes
cappaddy is a portable audio sample player with capacitive touch controls.
A work-in-progress prototype to try out different things with capacitive touch interfaces and learn more about ARM dev, STM32's peripherals (SPI, TIMER, DAC, DMA, TSC...) and some nice new parts like SPI flash, a new mono audio amp and a 3.3V voltage regulator/boost-converter, ideal for normal AAA cells.
- Microcontroller: STM32F072C8
- SPI Flash: Micron Serial NOR Flash Memory 128Mb - N25Q128A13EF740E-ND
- Amp: TI TPA2006D1 1.45-W MONO Filter-free Class-D Audio Power Amplifier with 1.8-V
- Voltage Converter: AS1337 Buck-Boost 200mA DC-DC step-up converter with buck mode
- 13 touch channels (15 when UART is not used)
- Small 8 Ohm Speaker
- 16 MB SPI Flash
- Powered by 2x AAA batteries
- Debug LED
- 5 GPIOs
- SWD Connector
- UART Connector
- Optional I2C connector
- Optional USB connector
Some pics are collected there.
Reference Value Digikey Count Unit SUM cappaddy:U1 STM32F072x8 STM32F072C8T6-ND 1 2.00 EUR 2.00 EUR (estimated) cappaddy:U2 N25Q128A13ESE40E 557-1562-ND 1 1.56 EUR 1.56 EUR cappaddy:U3 TPA2006D1 296-20783-1-ND 1 0.97 EUR 0.97 EUR cappaddy:U4 AS1337 AS1337A-BTDTCT-ND 1 1.46 EUR 1.46 EUR cappaddy:C13 C14 C17 1u 490-6423-1-ND 3 0.09 EUR 0.27 EUR cappaddy:C18 100pf 490-8180-1-ND 1 0.09 EUR 0.09 EUR cappaddy:C19 4u7 490-6407-1-ND 1 0.12 EUR 0.12 EUR 490-12606-1-ND 0 0.13 EUR 0.00 EUR cappaddy:C3-C6 C15 C16 100n 490-10777-1-ND 6 0.09 EUR 0.54 EUR cappaddy:C7-C12 1n 490-6349-1-ND 6 0.09 EUR 0.54 EUR cappaddy:R1-R3 R20 R27 R28 10k CR0402-FX-1002GLFCT-ND 6 0.09 EUR 0.54 EUR cappaddy:R4 470R CR0402-FX-4700GLFCT-ND 1 0.09 EUR 0.09 EUR cappaddy:R5-R19 1k CR0402-FX-1001GLFCT-ND 15 0.08 EUR 1.22 EUR cappaddy:R21-R23 100R CR0402-FX-1000GLFCT-ND 3 0.09 EUR 0.27 EUR cappaddy:R24 560k 1276-4267-1-ND 1 0.09 EUR 0.09 EUR cappaddy:R25 330k 1276-4244-1-ND 1 0.09 EUR 0.09 EUR cappaddy:R26 1M CR0402-FX-1004GLFCT-ND 1 0.09 EUR 0.09 EUR cappaddy:L1 4u7 490-6642-1-ND 1 0.39 EUR 0.39 EUR cappaddy:D1 LED 511-1651-1-ND 1 0.41 EUR 0.41 EUR cappaddy:battery_case BC2AAAL-ND battery_case 1 0.92 EUR 0.92 EUR speaker 1 1.50 EUR 1.50 EUR (estimated) 13.16 EUR
Note: The prices of the microcontroller and the speaker were estimated, those parts were ordered on Aliexpress (Speaker: Asus Padfone 2 A68 replacement).
Digikey BOM (MCU and speaker not included):
1,557-1562-ND,cappaddy:U2 1,296-20783-1-ND ,cappaddy:U3 1,AS1337A-BTDTCT-ND,cappaddy:U4 3,490-6423-1-ND ,cappaddy:C13 C14 C17 1,490-8180-1-ND ,cappaddy:C18 1,490-6407-1-ND,cappaddy:C19 6,490-10777-1-ND,cappaddy:C3-C6 C15 C16 6,490-6349-1-ND,cappaddy:C7-C12 6,CR0402-FX-1002GLFCT-ND ,cappaddy:R1-R3 R20 R27 R28 1,CR0402-FX-4700GLFCT-ND ,cappaddy:R4 15,CR0402-FX-1001GLFCT-ND,cappaddy:R5-R19 3,CR0402-FX-1000GLFCT-ND,cappaddy:R21-R23 1,1276-4267-1-ND,cappaddy:R24 1,1276-4244-1-ND,cappaddy:R25 1,CR0402-FX-1004GLFCT-ND,cappaddy:R26 1,490-6642-1-ND,cappaddy:L1 1,511-1651-1-ND,cappaddy:D1 1,BC2AAAL-ND,cappaddy:battery_case
To estimate the total price the PCB cost, casing, the touch pad material, optional power switch, 2 batteries, cables etc. need to be added (~20 Euros in total?).
The price as well as the amount of assembly work could be lowered A LOT by placing everything on one two-sided PCB. The touch pads, the tiny speaker (with a small tube on it?), the (coin cell) battery (enough capacity?). That would save one a lot of trouble with assembling the thing. A simple case to cover the sensitive parts can still be made. Another benefit of placing the touch pads on the PCB and cover it with solder mask is reliability I guess. Using custom pads with cables gives quite inconsistent results.
Get it at: OSH Park
KiCad project with gerbers, BOM spread sheet and schematics: cappaddy_rev0.03_local.zip
Errata / Known Bugs
- flash footprint wrong! -> can still be soldered by bending legs. -> or order the right QFN part next time...
- R23: audio amp ~SD pin: 100R -> 100K
- audio amp: input caps -> 3.3 nF
- audio amp: decoupling cap -> 1uF
- audio amp: input resistor -> 1MOhm (- 100k) -> 200k is not bad!
- spi / flash: pullup on chip select line and pull down on clock like in datasheet? (100kOhm)
- uart connector further apart from swd connector
- uart connector wider: for pin headers, not cables
- consider pin placement inside case! - where do the physical cables connecto to what? easy assembling!!!
- proper mounting holes
The case here just serves as a enclosure for the prototype. It is not really playable with touch pads like that.
Currently used/evaluated tools and sources to get the board up and running:
- Free tools for ARM development (gcc-none-eabi toolchain, OpenOCD)
- STM32CubeMX and sample code by ST
- Code::Blocks as an IDE
- Python with PySerial and matplotlib for scripting
More notes are collected here...
In the current test, every touch pad (13 in total) plays a sample stored in SPI flash. Reads it out, writes it to one of the two DMA buffers in RAM, and then the hardware moves the data (currently 8-bit samples at roughly 22 kHz -> trial and error tuning of TIM6) via DMA (double buffered) to the DAC and converts it there to an analog voltage.
(The code is a mess and has to be cleaned up. - I somehow like to go back to messy C coding, after spending lots of time with high level object oriented stuff, spending way too much time with plumbing and syntax cosmetics, making sure things are reusable and work everywhere. - Firmware can be messy I guess. Nobody cares when it works on the actual device. And there's only that specific hardware after all, so things will behave very deterministically. - However, that attitude will only work when things are tested out. It should be cleaned up, because I'm sure things will be tricky when I come back that project later... - Also, it makes sense to split things into files, use proper formatting, naming etc. Put things into functions... I've already hit the RAM limit of that tiny device by just putting in too many arrays into the spaghetti mess, causing the stack to overflow.. --- SO beware: this is only a proof of concept test, nothing more.
A word on STM docs and tools. Overall this STM32Cube tool is really useful generating the necessary initialization code and drivers (HAL) to work with. On the downside of that, I couldn't find any proper documentation on all the HAL functions, beside the comments in the source files and all the scattered ST example projects. That makes things quite annoying to work with. I actually really prefer to use those HAL functions instead of twiddling bits in all the registers myself. However, without proper documentation things feel more like a puzzle game in a cryptic world without a map. )
crude 12-bit version: cappaddy_TSC7.zip
Tool: Serial Monitor
After using alway Processing to display serial data, I've discovered a nice python library to draw all kinds of plots and visualize data, called matplotlib. It proved to be useful to display very basic serial debug data in realtime, like the 13 TSC channel values here:
Tmp script: plot_serial.py
Tool: Flash Downloader
In order to get data (audio samples) into the SPI flash on the board, the microcontroller has to play the middleman between the host PC and the flash. Things would be much easier with a standard SD card, but it was a fun exercise to get this working... The idea was, to make the microcontroller receive the data over UART, and send it down to the flash over SPI.
Again, Python is also a nice helper here to send out the sample data and talk to the hardware, using PySerial.
First, the samples have to be converted to raw 8-bit mono 22'050 Hz using SoX (later 12-bit samples should be used...), see below. Then the Python script takes the raw samples, and transmits the bytes in chunks of a certain size to the microcontroller, that is in flash programming mode. To keep the process error free, a CRC check on the received data and a verification of all written data to the flash is made. - First all that took quite a bit of time, and writing the full 16 MB of flash would have taken hours... But switching to the right flash programming commands (write flash pages (of 256 bytes) instead of single bytes for example), raising the serial baud rate to max 115200 and some other refinements took the programming time of a couple of kilobytes down to a reasonable point of a couple of seconds/minutes. - NOR flash memory has to be erased before it can be written (subsectors of 4K), and it is generally much slower to write to it, than to read from it. Since this has to be done only once to get the samples into the device it isn't really a problem. Reading from the flash afterwards goes surprisingly fast and is easier to do, as I had to find out...
All wav header data is stripped off the files before sending them out. Filename/identifier (8 characters), some info on the sample data (sample rate/depth) and most importantly the flash offset address is written to the first sector of the flash in table, in order to find the data later for playing it back...
The script can be started in the folder where the raw sample files reside while the board is listening in flash programming mode like that:
$ ./py_serial_send_wav.py -d /dev/ttyUSB0 serial 2 spi flash programmer started... in< CMD_numbytes_chunk?:256 chunk size set to 256 in< CMD_nextfile? ------------------------------------------------------------ current file (0): cartoon_0101.mp3_8bit_22050Hz_mono.raw out> OK in< ok in< CMD_filename? out> crtn0101 in< received filename: crtn0101 in< CMD_numbytes_total? out> 0069C0 in< number of bytes in file total: 0x0069C0 in< deleting flash subsector at 0x10000 in< flash subsector 1/7 deleted in< deleting flash subsector at 0x11000 in< flash subsector 2/7 deleted in< deleting flash subsector at 0x12000 in< flash subsector 3/7 deleted in< deleting flash subsector at 0x13000 in< flash subsector 4/7 deleted in< deleting flash subsector at 0x14000 in< flash subsector 5/7 deleted in< deleting flash subsector at 0x15000 in< flash subsector 6/7 deleted in< deleting flash subsector at 0x16000 in< flash subsector 7/7 deleted in< CMD_bytespls?:256 [ ] in< CMD_crc? out_byte> 0xEC in< CRC OK! in< writing 256 bytes to flash at 0x10000 in< CMD_bytespls?:256 [ ] in< CMD_crc? out_byte> 0xA8 in< CRC OK! in< writing 256 bytes to flash at 0x10100 in< CMD_bytespls?:256 [. ] (...)
in< CMD_crc? out_byte> 0x89 in< CRC OK! in< writing 64 bytes to flash at 0x65900 in< CMD_complete? out> OK in< transfer complete in< CMD_nextfile? ------------------------------------------------------------ out> NO in< finish in< found flash snd entries: 13 in< sound entry: 0 in< ---------------- in< index: 0 in< name: crtn0101 in< byte_count: 0x6a00 (26.5000 KB) in< start_address: 0x10000 in< sample_rate: 1 in< sample_depth: 0 in< in< sound entry: 1 in< ---------------- in< index: 1 in< name: crtn0104 in< byte_count: 0xe380 (56.8750 KB) in< start_address: 0x17000 in< sample_rate: 1 in< sample_depth: 0 in< in< sound entry: 2 in< ---------------- (...)
Tmp script: py_serial_send_wav.py
Tool: Sample Converter
Here a bunch of scripts to convert wav or mp3 files to raw samples (and also header files to paste right into your code):
It can be used like that. The .raw file is the right one to use with the flash programmer above:
$ ./convert_8bit_22050hz_mono.sh cartoon_1302.mp3 CONVERT cartoon_1302.mp3 WITH sox: -> ./conv_cartoon_1302.mp3/cartoon_1302.mp3_8bit_22050Hz_mono.wav -> ./conv_cartoon_1302.mp3/cartoon_1302.mp3_8bit_22050Hz_mono.raw CONVERT WITH CUSTOM PROGRAM (hexd): -> ./conv_cartoon_1302.mp3/cartoon_1302.mp3_8bit_22050Hz_mono.raw.hexd.txt CONVERT WITH xxd (nicer output): -> ./conv_cartoon_1302.mp3/cartoon_1302.mp3_8bit_22050Hz_mono.raw.xxd.txt
Remarks / Conclusions
So far, all the used parts seem to work fine and I'd use them again.
There are still lots of issues that need to solved. For example, the SPI flash has some strange behavior when writing to it (write in progress flag keeps up). The STM32 TSC peripheral has not been tuned yet (very low res measurements), and the DAC playback sample rate was tuned by trial'n'error. There were some DMA underruns when switching samples fast... Some kind of debouncing/hystersis for the touch pads should be added, and the reading should be averaged to make play smoother... Volume control, dynamic fadeout, + other stuff.
The project is probably on halt for a while. Basic facilities are working now...
- 1 huge sample + offset test
- change playback rate (timer)
- REX style loop player
- loop samples + fadeout? loop points?
- add button to change playback mode/preset etc.
- add leds for feedback?
- add pot / wheel for volume control
- add audio jack for line out?
- 1 pcb. 2 sided. touch pads and speaker and battery on it. CHEAP?
- sd card instead of SPI flash?
- resistive pads instead of capative?
- capacitive pads: easy to breakout -> makey makey style? clips?
- sequenced play. record?
- case / touch pads: make completely new concept.
- case: make wire hole bigger.
- case: rubber material?
- increase TSC resolution, tune it!
- make 12bit version
- add hysteresis for better play.
- make play smooth. no stutters.
- 1 time calibration?