Go Down

Topic: Steps to get ATTINY85 bipooar ADC & I2C slave working (Read 4881 times) previous topic - next topic

arduinoCoder

Dec 19, 2013, 09:25 am Last Edit: Dec 22, 2013, 10:30 pm by arduinoCoder Reason: 1
Hi All

This is something I struggled with so I thought I would share my experience and how I went about solving the issues I had.  My aim was to implement a sensor which would trigger if it detected any noise above a set threshold.  It is part of a bigger system which makes use of an I2C bus, so I had to get that working on the ATTINY85 as well.  I hope this will help anyone trying to use a ATTINY in Arduino.

My development PC is an Intel I5 running Windows 7 Pro.

A summary of what I did:

  • Get the AVR ISP MkII and attiny85 to work from Arduino IDE

  • Get the I2C interface working

  • Get the bipolar ADC on the tiny up and running


AVR ISP MkII and Arduino
Very debated topic.  I first tried it with 1.5.5 but eventually gave up.  I installed the latest libusb-win32 drivers for it and it seemed to work until I connect it to the circuit.  When it it not connected to the board, avrdude gives a status: MOSI fail, SCK fail which is to be expected.  As soon as it is connected to the circuit, it gives the classic did not find any USB device "usb" error associated with incorrect drivers.  This baffled me for some time until I eventually gave up and moved to 1.0.5 which works fine, which suggests that there is something broken in 1.5.5.

I did not try any other versions as I did not have too much time to fiddle around.

ATTINY85 and Arduino
This went hand in hand with the programmer.  I made use of the "google" tiny core called arduino-tiny available from http://code.google.com/p/arduino-tiny/.  The core I used was version 0100-0018.  Just follow the instructions contained in the README file.  From Prospective Boards.txt I just took the attiny85 running at 8MHz internal oscillator to create the boards.txt file.  That is the IDE done and dusted.  

The Arduino pin names are described in cores\tiny\pins_arduino.c.  REMEMBER TO DO A "BURN BOOTLOADER" BEFORE UPLOADING YOUR SKETCH.  This will set the correct fuse bits for the internal oscillator, etc.  If you change to a different board (chip/freq), you will have to burn bootloader again.  To upload the sketch, hold down SHIFT and click on upload, or select Upload Using Programmer from the file menu (I make use of the file menu option since my free hand holds down reset on my I2C bus master UNO to free up the I2C lines during programming).

I2C Slave Implementation
I first tried using the USIi2c available from http://playground.arduino.cc/Code/USIi2c, but I found it not to work reliably.   In my final solution I used the latest version from https://github.com/rambo/TinyWire where the onRequest and onReceive were implemented and that works 100%.

Bipolar ADC Implementation
The final part of the project was to connect a condenser microphone to the ADC inputs of the tiny85 and set the ADC to bipolar mode.  After studying the core files and the datasheet, the solution was fairly simple.  Just set the BIN bit in ADCSRB, set the reference to internal 1.1V and give the correct channel number to analogRead() in the core libraries.  There was no need to change any of the core libraries, though it is my plan to change it to support differential inputs directly.  The only trick with the unmodified core is to select channel number 13 if you are using A2 and A3 for differential input.  The way to determine the channel number is to go look in core_adc.h, convert the binary value next to the configuration you need to decimal (in my case Pos2_Neg3_20x = B0111 which is 7) and add 6.  The value of 6 is because the arduino analog pin numbers come after the digital pins, and the attiny has 6.

On the hardware side of the ADC in bipolar mode, it is important that the internal 1.1V reference is used.  The ADC can behave funny if the reference is left at DEFAULT.

Code for my ATTINY85 noise trigger:
Code: [Select]
#include <TinyWireS.h>

const byte I2C_SLAVE_ADDR = 0x5;
const byte LED_PIN = 1;
int adcReading = 0;
int adcMax = -1000;
int adcMin = 1000;

int triggerLevel = 60;
byte triggerCount = 0;

volatile byte i2cReadPos = 0;
volatile byte i2cBuf[4];

void setup() {
 pinMode(LED_PIN, OUTPUT);
 
 TinyWireS.begin(I2C_SLAVE_ADDR);
 TinyWireS.onReceive(receiveEvent);
 TinyWireS.onRequest(requestEvent);
 
 analogReference(INTERNAL);
 ADCSRB|= 0x80;      // enable bipolar input mode
}

void loop() {
 adcReading = analogRead(7 + CORE_ANALOG_FIRST);    // bipolar input channels 2 and 3 20x gain
 if (adcReading > 511) adcReading-= 1024;
 if (adcReading > adcMax) adcMax = adcReading;
 if (adcReading < adcMin) adcMin = adcReading;
 if (triggerCount < 255){
   if (adcReading < -triggerLevel){
     triggerCount++;
   } else if (adcReading > triggerLevel){
     triggerCount++;
   }
 }
 digitalWrite(LED_PIN, triggerCount ? HIGH : LOW);
}

void requestEvent() {  
 if (i2cReadPos){
   TinyWireS.send(i2cBuf[--i2cReadPos]);
 } else {
   TinyWireS.send(0);
 }
}

void receiveEvent(byte howMany){
 if (howMany < 1) return;
 if (howMany > 5) return;
 while(howMany--){
   switch (TinyWireS.receive()){
     case 1:   // get current adc reading
       i2cBuf[1] = adcReading >> 8;
       i2cBuf[0] = adcReading & 0xFF;
       i2cReadPos = 2;
       break;
     case 2:   // get Min/Max values
       i2cBuf[3] = adcMin >> 8;
       i2cBuf[2] = adcMin & 0xFF;
       i2cBuf[1] = adcMax >> 8;
       i2cBuf[0] = adcMax & 0xFF;
       i2cReadPos = 4;
       adcMin = 1000;
       adcMax = -1000;
       break;
     case 3:    // set trigger level
       triggerLevel = TinyWireS.receive() * 256 + TinyWireS.receive();
       break;
     case 4:   // get trigger count
       i2cBuf[0] = triggerCount;
       i2cReadPos = 1;
       triggerCount = 0;
       break;      
   }
 }
}


Coding Badly


Thank you for posting about your experience.

Quote
...though it is my plan to change it to support differential inputs directly.


In what way?

arduinoCoder

#2
Dec 19, 2013, 10:30 am Last Edit: Dec 19, 2013, 10:40 am by arduinoCoder Reason: 1
My plan is so you can use it as you would any analog channel, for instance:
Code: [Select]
int diffReading = analogRead(A2_A3_20X);
and that the function will setup the ADC for bipolar input, select the correct channel and convert the reading to a int (in stead of the 10 bit 2's complement number).

Coding Badly


Did you try this...
Quote
Then, if the user wants to perform the conversion with the maximum dynamic range, the user can perform a quick polarity check of the result and use the unipolar differential conversion with selectable differential input pairs (see the Input Polarity Reversal mode ie. the IPR bit in the "ADCSRB - ADC Control and Status Register B" on page 136). When the polarity check is performed, it is sufficient to read the MSB of the result (ADC9 in ADCH). If the bit is one, the result is negative, and if this bit is zero, the result is positive.


Do you have an interest / need in getting the full 10 bits of resolution?  (It looks like the IPR bit comes into play.)

arduinoCoder

I saw that going through the datasheet, but decided not to make use of it as it will halve the sampling rate and I need an as high as possible bandwidth to catch high frequency noise .   As I am only using it as a peek detector, I can live the reduced resolution.

I have made changes to the core libraries to support bipolar sampling (not yet the inverted channels) and can make the changes available.  It is a couple of lines in pins_arduino.h and wiring_analog.c.   I have only implemented it for the attiny85, but will soon start using the attiny84 and implement it for that as well.

retrolefty

#5
Dec 20, 2013, 04:23 pm Last Edit: Dec 20, 2013, 04:27 pm by retrolefty Reason: 1
I'm a bit confused about your use of the term "bipolar input" Vs "differential" used in the subject title.

Bipolar implies being able to read negative voltages relative to arduino ground potential which is not really possible as any value less then ground will only return a ADC result of 0 counts and if the signal is large enough negative there is a chance of turning on the negative clamping diode and risking pin damage.

The term differential mode in context with a single supply AVR chips is only differential in the sense that one can measure the differences between two positive voltages applied to two analog input pins, but both pins may only 'see' a absolute voltage in the range of 0 to (Vcc+.5vdc) at all times, it's an electrical requirement.

A unconditioned microphone signal is a low level AC voltage and as such not able to be processed correctly with a arduino analog input pin without external circuitry to add the proper DC offset voltage required to keep the voltage limited to only zero to positive values.

Lefty

arduinoCoder

Lefty

I used bipolar and differential as the same thing...

The ADC on the attiny processors is different to the one in the mega. It has a bipolar mode where two channels can be measured relative to each other and there is even a 20x gain which can be enabled. For my application I used analog channels 2 and 3 with the gain enabled and internal 1.1V reference. This gives a measurement resolution of about 100uV which was good enough for my application. I did make use of a resistor divider to get the common mode voltage to within the analog supply voltage which was 3.3V in my circuit.

Refer to the attiny data sheet (page 133) for the formulas associated with bipolar differential conversion.

I built the circuit vero board and will post the schematics as soon as I get a chance to draw them.


retrolefty

I'm still of the opinion that bipolar implies the ability to measure a negative voltage relative to chip ground, which is not allowed for electrical reasons for the AVR chips. Whereas differential just means the ability to read the difference between two analog signals. In the hardware world the difference (sorry) between these two words is significant.




Go Up