My journey with ATtiny4313
The context
For a personal project, I want to program an Atmel ATtiny4313 microcontroller. I use microcontrollers for several years, but not on a regular basis (maybe 3-4 times a year), mostly for testing and generally with a deceptive result. But failure isn't the best training?
This article aims to share my errors and success, as well as being a personal log.
The environment
Here is the environment I'm using:
- Linux Ubuntu 20.04
- Arduino IDE 1.8.19 installed as snap (containerized application)
- Arduino Mini rev 5
- Arduino usb2serial board
- Breadboard
Programming the Programmer
The idea is to program the Arduino Mini to behave as an ISP, In-System Programmer, compatible with STK500. Once done, the ISP will communicate with the ATtiny via the SPI port.
- Place the components on the breadboard and do the wiring as follows:
Note: To have a better timing, I had to add an 8 MHz crystal between pins 4 and 5, as well as 2 * 15pF capacitors. Refer to section 6.2.5 of the ATtiny 4313 datasheet. I also added a 100nF ceramic disk decoupling capacitor between pins 10 and 20, over the chip; maybe not strictly required, but at least it couldn't harm.
- Start Arduino IDE
- Select the board (in my case Arduino Mini)
- Select the programmer (in my case AVR ISP mkII)
- Load the sketch: File -> Examples -> 11.ArduinoISP -> ArduinoISP
- Edit the sketch if using the Arduino Mini: pin 11 and 12 must be swapped. The pinout of Arduino Mini is:
Pin 10 = SS (used for RESET) Pin 11 = MISO Pin 12 = MOSI Pin 13 = SCK
- Click on upload
- Exit Arduino IDE
Install the Arduino-Tiny library
- Download the library from github.
- Extract it at the right place. In my case, it was:
~/snap/arduino/85/Arduino/hardware
. It will create the foldertiny
. - Follow the instructions (also available here) to create
boards.txt
. - I had to fix two lines in boards.txt:
... 406 attiny4313at8.name=ATtiny4313 @ 8 MHz 407 408 attiny4313at8.upload.tool=arduino:arduinoisp 409 attiny4313at8.upload.using=arduino:arduinoisp ... 434 attiny4313at1.name=ATtiny4313 @ 1 MHz 435 436 attiny4313at1.upload.tool=arduino:arduinoisp 437 attiny4313at8.upload.using=arduino:arduinoisp ...
Burn the ATtiny bootloader
As stated in Oscar Liang's blog, there is no real bootloader but simply a setting of fuses. However, this was the trickiest part which took many trials and errors. The standard way to proceed is :
- Start Arduino IDE
- Select the board: ATtiny4313 @ 8 MHz
- Select the programmer: Arduino as ISP
- Load the sketch: File -> Examples -> 01.Basic -> Blink
- Click on Tools -> Burn Bootloader
- Click on File -> Preferences -> Show verbose output during: and check both compilation and upload
avrdude: Device signature = 0x000000 avrdude: Yikes! Invalid device signature. Double check connections and try again, or use -F to override this check.What to do with that? Actually, the Arduino IDE uses avrdude to program the chip; we can use avrdude directly (without the IDE) to have more controls.
- Open a terminal
- To ease my trial-and-error, I defined an alias and a variable:
$ alias avrdude='/snap/arduino/85/hardware/tools/avr/bin/avrdude -vvvv' $ export CONF=/snap/arduino/85/hardware/tools/avr/etc/avrdude.conf
- Then, I was able to start avrdude like this:
$ avrdude -C ${CONF} -p attiny4313 -c stk500v1 -P /dev/ttyACM0 -b19200 -t
The meaning of options are:-vvvv <- very verbose -p attiny4313 <- type of target -c stk500v1 <- programmer id. -P /dev/ttyACM0 <- USB port -b 19200 <- baudrate -e <- erase the chip (1) -Uefuse:w:0xFF:m <- configure the extended fuse byte (2) -Uhfuse:w:0x9F:m <- configure the high fuse byte (3) -Ulfuse:w:0xE4:m <- configure the low fuse byte (4)
Notes:- actually fill the flash and the EEPROM with 0xFF
- The values configured for Extended fuse are
Bit Value Bit name Description 7 1 Bit 7 (unprogrammed) 6 1 Bit 6 (unprogrammed) 5 1 Bit 5 (unprogrammed) 4 1 Bit 4 (unprogrammed) 3 1 Bit 3 (unprogrammed) 2 1 Bit 2 (unprogrammed) 1 1 Bit 1 (unprogrammed) 0 1 SELFPRGEN Disable Self-Programming instruction
- The values configured for High fuse are
Bit Value Bit name Description 7 1 DWEN Disable debugWIRE 6 0 EESAVE Allow EEPROM to be erased 5 0 SPIEN Enables serial programming and downloading of data to device 4 1 WDTON Sets watchdog timer permanently on 3 1 BODLEVEL2 Brown-Out Detection level: 2 1 BODLEVEL1 111 = disabled 1 1 BODLEVEL0 0 1 RSTDISBL Do not disable external reset
- The values configured for Low fuse are
Bit Value Bit name Description 7 1 CKDIV8 Do not divides clock by 8 (hence 8 MHz) 6 1 CKOUT Output system clock on pin 6 5 1 SUT1 Start-up time: slow rising power (default). 4 0 SUT0 (6 + 14 clock ticks + 64 ms) 3 0 CKSEL3 Clock select: 2 1 CKSEL2 0100 = Calibrated internal RC Oscillator 1 0 CKSEL1 (8 MHz) 0 0 CKSEL0
- I had to reset the Arduino a couple of time, due to incorrect timing, but I always had errors. Then I setup the fuses manually like this:
$ avrdude -C ${CONF} -p attiny4313 -c stk500v1 -P /dev/ttyACM0 -b19200 -F -t avrdude> sig >>> sig Device signature = 0x1e920d
Yes!!! This is the signature of ATtiny4313, as noted in the datasheet:For the ATtiny4313 the signature bytes are:
Now, we can setup the fuses:
1. 0x000: 0x1E (indicates manufactured by Atmel).
2. 0x001: 0x92 (indicates 4KB Flash memory).
3. 0x002: 0x0D (indicates ATtiny4313 device when 0x001 is 0x92)avrdude> d lfuse <- dump the value of lfuse 0000 64 |d | <- current value avrdude> w lfuse 0 0xe4 <- set the value to 0xE4. I had to change the value later (see below). avrdude> d lfuse <- check the value again 0000 e4 <- Yes!! avrdude> w hfuse 0 0x9f <- set the value to hfuse avrdude> d hfuse 0000 9f <- Ok avrdude> d efuse 0000 ff <- Already the good value
Blink
I put a LED and resistor on pin 9, and add the following line to blink.ino:
#define LED_BUILTIN PIN_D5Blink did upload successfully and my LED started to blink. Yess!!
And now let's do MIDI
The purpose of this project is to replace a dead Soundpool MO4. In short, four ATTiny (one per MIDI out) must read the incomming data on the parallel port (MIDI message), determine who will manage the message and send it to the output via the USART.
The wiring
I decided to use a quad tri-state buffer (SN74LS125) to feed the MIDI current loop. The datasheet says to keep sinking below 24 mA, but does not mention if for the entire unit of per gate. Anyway, since the MIDI electrical characteritics says the current loop must be 5 mA, we are good. However, this means feeding the loop thru 2 * 500 ohm resistors, instead of 2 * 220 ohm resistors that we commonly see in schematics. Currently, I use 220 ohm since I use only one gate.
The schematics is:
The code
This is only a test code which plays a 8 note melody.
#include <avr/io.h> // A little MIDI melody uint8_t melody[8] = {0x24,0x27,0x2b,0x2d,0x30,0x2d,0x2b,0x27}, note=0; void setup() { UBRRH = (uint8_t)0x00; // Set UBRR to divide by 16 in normal mode which in reality ... UBRRL = (uint8_t)0x0f; //... divides the system clock (8MHz) by 256. 8000000 / 256 = 31250 UCSRB = (1<<TXEN); // Enable the transmitter, but not the receiver UCSRC = (1<<UCSZ1|1<<UCSZ0); // 8 bits, no parity, 1 stop bit } void MIDIout(uint8_t n) { // Play midi melody uint8_t i=(n & 0x07); UDR=(uint8_t)0x90; // Note ON channel 1 UDR=melody[i]; // Note UDR=(uint8_t)0x40; // Velocity delay(500); UDR=(uint8_t)0x80; // Note OFF channel 1 UDR=melody[i]; // Note UDR=(uint8_t)0x00; // Velocity } void loop() { MIDIout(note++); }Compile, upload and ... nothing! My synth stays silent. What's going on?
Hopefully, I have a digital oscilloscope (DSO), the fairly common Rigol DS1054z. Thanks to this video from Ben-tronics, I was able to find the issue:
A MIDI byte should be sent in 320 micro-seconds; in my case it span over 398 micro-seconds, way over the 1% precision required by the MIDI specs. The reason: I use the ATTiny internal oscillator, which is obvioulsy not calibrated. So I must change the low fuse and use an external crystal.Adding a crystal
I added a 8 MHz crystal with 2 * 20 pF capacitors (the datasheet says 12 to 22 pF) and I changed the low fuse to sync the internal clock to the crystal:
$ avrdude -C ${CONF} -p attiny4313 -c stk500v1 -P /dev/ttyACM0 -b19200 -t (...) avrdude> w lfuse 0 0xcf >>> w lfuse 0 0xcfWith this change, my clock was tight and every byte has the correct timing: I receive the note on (0x90) and note off (0x80), the following byte corresponds to the note from my melody, all right! But wait... Where is the third byte (velocity)? I did many tries, checked my code and the breadboard, but no luck: the third byte is always missing.
Don't send too fast...
A little glance at the datasheet on page 127 provides the explanation: you must wait for completion before sending a new byte. I was simply sending too fast. I fix the code, and the third byte appeared:
(...) void Send(uint8_t data ) { while ( !( UCSRA & (1<:<:UDRE)) ) ; UDR = data; } void MIDIout(uint8_t n) { // Play midi melody uint8_t i=(n & 0x07); Send(0x90); // Note ON channel 1 Send(melody[i]); // Note Send(0x40); // Velocity delay(500); Send(0x80); // Note OFF channel 1 Send(melody[i]); // Note Send(0x00); // Velocity delay(500); } (...)But still no sound out of my synth (a Roland Sound Canvas SC8850). Hmm, what going on?
I reset the synth to its factory default, but still nothing. The synth works as I'm able to get the audio preview (by pushing the volume knob), but nothing when driven by MIDI, no matter which entry. I tested with another cable and another synth (a Novation XioSynth), checked the channels and the configuration on both, but I was not able to get any sound... I suspect both MIDI input opto-isolator (Sharp PC410) has burned during my testing. They both show 32 MOhm resistance on reverse, infinity on direct. Grr!
And finally...
To avoid another disaster, I used a MIDI Thru box (Korg KMT-60) between my breadboard and my synth. I connect another synth, set up the MIDI channel, and ... the little melody played!
Comments