The first part of this week is mostly reading and understanding what's happening within the ATTinys. I'll go over what I got from the chapters of this quite long datasheet.
PORTA
and PORTB
.SPH
and SPL
for the stack pointer, and SREG
for the global AVR status.EEARH
and EEARL
for EEPROM addressing, EEDR
for the EEPROM data to read/write, EECR
for controlling the write/read access mode of EEPROM, and finally GPIORx
(x=0,1,2) for general-purpose input/output registers.CKSEL
for the type of clock to use, SUT
for startup timing, and CKDIV8
for the initial state of the prescaler (frequency divided by 8 by default).CKSEL
for the type of clock to use, OSCCAL
for oscillator calibration, and CLKPR
for the clock prescaler.MCUCR
for controlling the type of sleep mode when using SLEEP()
, and PRR
for further power reduction abilities, as well as DIDR0
for disabling digital input on analog pins.MCUSR
for flags describing which source triggered the reset (watchdog, external, brown-out or power-on), and WDTCSR
for watchdog timer control and status.0x00
(reset, by default) to 0x10
.MCUCR
MCU control register with two last bits for mode of the external interruption on pin INT0 (level = low or any level, edge = on falling or rising edge), and GIMSK
, GIFR
and PCMSK0/1
for enabling some interruptions (beyond register SREG
from Chapter 4).PORTA = (1 << PA1) | (1 << PA2); DDRA = (1 << DDA1);
).
Need for synchronization after switching direction (i.e. needs _NOP()
or something else before reading from port after direction change).
List of alternate functions for ports including analog functions, interrupt functions, USI, and SPI on port A; crystal, interrupt, clock output, debug wire and reset on port B.
Port diagrams separating PORTXn, DDRXn and PINXn for data write/read, mode, and reading/toggling value.MCUCR
with global pull-up disable bit, PORTA
, DDRA
and PINA
and similar counterparts for Bfclk_ext < fclk_I/O/2.5
.
TCCRnx
(x=A,B,C and n=0 for 8-bits and n=1 for 16-bits registers) for the timer-counter controls (compare-match output modes, and waveform generation modes); TCNTn
for direct read/write access to the timer counter; OCRnx
for the value to compare and match (for interruptions and/or waveform generation); TIMSKn
for timer interruption masks, and TIFRn
for interruption flags / states.
Finally, GTCCR
provide synchronization bits for the counter/timer and their prescaler.USICR
for enabling interrupts, setting both wire and clock modes, USISR
for flags, line status and counter value, USIDR
where the transfer data is stored / received, and USIBR
as read buffer.ACSR
for control and status to enable, select modes of the comparator, and read interrupt flags, ADCSRB
for using a specific pin of the ADC instead of AIN1, and DIDR0
to disable digital input buffers for AIN1/0 and thus reduce power consumption if not needing digital input functions.ADC=1024 VIN/VREF
going from GND to Vref, Unipolar Differential Conversion ADC=1024 × GAIN × (VPOS - VNEG) / VREF
with GAIN either 1 or 20 (36dB), or Bipolar Differential Comparison ADC=512 × GAIN × (VPOS - VNEG) / VREF
.
Finally, ADC can be used to read temperature using an on-chip temperature sensor using ADC8 channel.ADMUX
for selecting the voltage reference, the combination of analog inputs, and the gain to use; ADCSRA
for enabling the ADC, starting conversions, enabling auto-trigger as well as interruptions and the corresponding flag, and selecting the ADC clock prescaler; ADCL/H
containing the 10-bits conversion result; ADCSRB
for selecting the auto-trigger source, result alignment and bipolar input mode (defaults to unipolar); and DIDR0
for disabling digital input on ADC-able pins.SELFPRGEN
bit for programming;
Fuse High Byte as RSTDISBL,DWEN,SPIEN,WDTON,EESAVE,BODLEVEL1/2/3
for disabling external reset, enabling debugwire, enabling program download, setting whether watchdog timer is always on, whether EEPROM stay beyond programming, and the Brown-Out level; Fuse Low Byte as CKDIV8,CKOUT,SUT1,SUT0,CKSEL3/2/1/0
for dividing the clock frequency by 8, enabling clock output pin, selecting the startup timing, as well as the type of clock.
Finally, the Device Signature Byte can be used to figure out which device we're dealing with (ATTIny 24/44/84).I first decided to test led and switches on my first board. Finding the correct constants is obvious given the datasheet (although it looked all completely mysterious before I had read it). In this case, I am using the two pins on the left side before the last at the bottom, i.e. PB2 for the led and PA7 for the switch.
The output corresponds to setting DDRB = 1 << PB2;
and then outputting the desired voltage bit (low = 0, or high = 1) with PORTB = 1 << PB2;
.
The input from the switch is connected to ground when not pressing, and disconnected when pressing (counterintuitive to me).
This means that we want to use a pullup resistor, which we activate with PORTA = 1 << PA7;
(without changing DDRA since default is input).
Now, whenever we press the button, the reading from PINA
will be 0, and by default it will be 1 << PA7
.
I tested multiple variants (modes) of connecting the switch and the led:
#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
typedef enum {
ON_OFF,
STATEFUL,
BLINKING,
CHANGING,
PULSING
} SwitchMode;
int main(void) {
//
// main
//
static unsigned char switch_curr, switch_last;
static unsigned char state;
static unsigned char counter;
static unsigned char i;
SwitchMode mode = PULSING;
//
// set clock divider to /1
//
CLKPR = (1 << CLKPCE);
CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
//
// initialize output pins
//
PORTA = (1 << PA7); // enable pull-up for switch input
DDRA = 0; // all inputs on port A
PORTB = (1 << PB2); // enable output for led
DDRB = (1 << PB2);
// synchronize
_delay_ms(1);
// initial state
switch_curr = switch_last = PINA & (1 << PA7);
state = switch_curr;
//
// main loop
//
while (1) {
// get switch state
switch_curr = PINA & (1 << PA7);
switch(mode){
case ON_OFF:
// use switch to decide whether to have led on or off
state = switch_curr;
break;
case BLINKING:
// just toggle state
state = !state;
_delay_ms(100);
break;
case CHANGING:
// toggle but change the delay from counter
state = !state;
if(counter >= 100)
counter = 0;
else
counter += 1;
for(i = 0; i < counter; ++i)
_delay_ms(1);
break;
case PULSING:
// toggle but change the delay from counter
state = !state;
// change off-timing
if(state){
if(counter > 200)
counter = 0;
else
counter += 5;
for(i = 0; i < counter; ++i)
_delay_us(100);
}
break;
case STATEFUL:
default:
// use change of state to invert led state
if(switch_curr != switch_last && !!switch_last)
state = !state;
break;
}
switch_last = switch_curr;
// led output
if(state)
PORTB = 1 << PB2; // PORTB |= (1 << PB2); // led on
else
PORTB = 0; // &= ~(1 << PB2); // led off
// wait a bit
if(mode == PULSING)
_delay_ms(1);
else
_delay_ms(1);
}
}
The recitation session over USB programming was very interesting and triggered my interest. I thus looked for tutorials beyond what Mike Specter had talked about and found the long detailed work of Andrew Mao who used an ATMega16U2 with Hardware USB (for easy programming over USB) to program his board to be a basic keyboard. One idea that went through my head was to play pong with the mouse, and potentially use the keyboard for extra inputs and/or outputting results and help information. This is beyond the class, but an interesting project nonetheless. I only went up to verifying that I can have the USB board run with hardware USB enabled, proving that something can be done.
For the board, I decided to use similar components to the ones of Andrew as a first attempt. The one component we do not have in stock is the 16 MHz crystal. At first, I asked Gavin and eventually we found out that there were extra crystals in the EDS stock, some of which were 16 MHz ones. I looked for the model on digikey and found the reference PLE SRMP49 which corresponds to a 16MHz crystal in series resonance. At first, I was slightly suprised since all crystals I had seen explanations for had a load capacitance that had to be matched "in parallel", and had made my design following this assumption (similarly to Andrew and Mike). Seemingly, multiple crystal circuit types exists including the more common "parallel resonance" and this less common "series resonance" that does not require any extra capacitor. However, at the same time, one of my colleague who I had asked for a 16 MHz crystal answered that he had a defective Arduino Uno I could harvest the crystal from. As a first attempt, I am going with the Arduino one since it's proven to work.
The second difference was the LED which, although very similar, looks slightly different. I found the reference CLV1A-FKB for the part in our stock and it seems that the orientation is slightly different. I ended up with the wrong orientation initially when populating the components, but easily swapped the corresponding resistor for the red led as I understood the correct orientation. This was my first personal usage of the heated tweezer, and it was quite fun and easy!
For the Eagle schematics, I wanted to use the USB pad, which was supposed to be in an extra SparkFun library. Eagle has a way to download these extra libraries and includes the "SparkFun" component library, or ... so I thought. Unfortunately, the library it downloads is actually not up to date. I had to download the one from the github repository to find the correct USB pad component in SparkFun-Connectors.lbr. At this point, I traced and cut the PCB. No major issue.The soldering was less tricky than I thought. As suggested on some tutorials, I positioned the ATMega part first and then taped it to ensure I could more precisely align it before fully fixing it and soldering its pads. I eventually had solder connect multiple pads as expected and I tried to use the wick without any result again... I still can't use it propertly! However, this time I tried the slurping hot desoldering gun next to the tweezer and this is now my favorite desoldering tool. It does exactly what I want (and even slightly more intensely than I'd like it to, but removing too much is fine since adding solder back is much easier). I then tried to test the board by plugging it straight into a computer. Most computers I tried would trigger the green led showing that power was available, but they would not detect it as it should have (as DFU device). Debugging time!
The first check was whether anything was shorted. Nothing seemingly was.
The second check was whether the power was distributed correctly and it seemed to be (5V between VCC and GND).
Then, I decided to check the clock signal (if it was possible to see it in action) using an oscilloscope. I didn't know whether the crystal was working since it could well have been the reason the Arduino I took it from was not working. That's where I saw that ... I saw nothing. I then probed the capacitors with the multimeter to check that my stacked 10 pF capacitors were summing correctly to 20 pF. As I was told later, measuring the capacitance cannot be done with a multimeter unless you extract the component since the remaining parts of the circuits influence the measured capacitance. However, this did show me the first problem which was that my capacitors were several of magnitude wrong. Indeed, I had picked 10 uF ones instead of the picofarad ones I should have!
I removed the stacked capacitors next to the crystal using the hot tweezer, and then checked again with the correct ones. The signal was now as expected! The only unexpected part was that the voltage to ground was only ~1.3V. This could have been an issue with power, but as it turns out, this is what I also measured on another Arduino UNO, so I assume this is how they work.
At this point, it was sadly not yet recognized as DFU device and I suspected that the data pins were not connecting. I decided to create a small USB pad extension (quickest milling done so far!). For this board, I designed it the simplest way with Gimp and using the trace of the previous board as reference for the pad size. The connection was done using a 4-pin header which I soldered on both sides.
At this point, the board shows up as DFU device! The rest is for another time.