Code Snippets
Feb 25th, 2012 by Alex
The code snippets below are intended for Atmel AVR MCUs.
Watchdog Timer Delay function
Puts your MCU to sleep using the watchdog for the time you specify.
Usage: watchdogSleep(T250MS);
#define T16MS 0
#define T32MS 1
#define T64MS 2
#define T128MS 3
#define T250MS 4
#define T500MS 5
#define T1S 6
#define T2S 7
#define T4S 8
#define T8S 9
// Used from http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/
void system_sleep(void) {
cbi(ADCSRA,ADEN); // Switch Analog to Digital converter OFF
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode
sleep_mode(); // System sleeps here
sbi(ADCSRA,ADEN); // Switch Analog to Digital converter ON
}
// Watchdog timeout values
// 0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms, 6=1sec, 7=2sec, 8=4sec, 9=8sec
// Used from http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/
void setup_watchdog (int ii) {
byte bb;
if (ii > 9 ) ii=9;
bb=ii & 7;
if (ii > 7) bb|= (1<<5);
bb|= (1<<WDCE);
MCUSR &= ~(1<<WDRF);
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
WDTCR = bb; // Set new watchdog timeout value
WDTCR |= _BV(WDIE);
}
// Turn off the Watchdog
void turnOffwatchdog (void) {
cli(); // Turn interrupts off
MCUSR &= ~(1<<WDRF);
WDTCR |= (1<<WDCE) | (1<<WDE);
WDTCR = 0;
sei(); // Turn interrupts back on
}
// Watchdog sleep function - combines all the above functions into one
void watchdogSleep (int timeout){
setup_watchdog(timeout);
system_sleep();
turnOffwatchdog();
}
ISR(WDT_vect) { }
.
Pin Change Interrupt
ISR(PCINT0_vect) will be run each time the pin goes high to low or low to high
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
sei(); // Turn on interrupts
// Pin interrupt setup
sbi(GIMSK,PCIE); // Enable pin change interrupt
sbi(PCMSK,PCINT4); // Apply interrupt to this pin
ISR(PCINT0_vect) {
// Your code here
}
.
ATtiny85 Timer1 Overflow with 32.768 KHz clock
ISR(TIMER1_OVF_vect) will be run when Timer1 overflows which is every second with a 32.768 KHz clock
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
sei(); // Turn on interrupts
// Set prescaler to 128 to give exactly 1 second before an overflow occurs.
// 128 prescaler x 256 timer bits / 32768 clock = 1 second
sbi(TCCR1, CS13);
// Enable timer 1 overflow interrupt
sbi(TIMSK, TOIE1);
// Timer 1 interrupt
ISR(TIMER1_OVF_vect) {
// Your code here
}
.
Read a 10K Thermistor
Reads a 10K thermistor with a 10k resistor as the resistor divider and returns the temperature
Usage: int tempValue = (int) thermistorTemp(analogRead(thermistorPin));
void setup(void) {
sei(); // Turn on interrupts
// Set ADC prescale factor to 8
// 1 MHz / 8 = 125 KHz, inside the desired 50-200 KHz range.
sbi(ADCSRA, ADPS1);
sbi(ADCSRA, ADPS0);
// Enable ADC conversions
sbi(ADCSRA, ADEN);
}
// Used from Arduino wiring_analog.c
int analogRead(uint8_t pin) {
uint8_t low, high;
// set the analog reference (high two bits of ADMUX) and select the
// channel (low 4 bits). this also sets ADLAR (left-adjust result)
// to 0 (the default).
ADMUX = pin & 0x3f;
// start the conversion
sbi(ADCSRA, ADSC);
// ADSC is cleared when the conversion finishes
while (bit_is_set(ADCSRA, ADSC));
// we have to read ADCL first; doing so locks both ADCL
// and ADCH until ADCH is read. reading ADCL second would
// cause the results of each conversion to be discarded,
// as ADCL and ADCH would be locked when it completed.
low = ADCL;
high = ADCH;
// combine the two bytes
return ( high << 8 ) | low;
}
// Modified from http://www.arduino.cc/playground/ComponentLib/Thermistor2
// Learn more at https://www.insidegadgets.com/2011/12/18/verifying-the-thermistor-function-for-temperature-reading/
double thermistorTemp(int RawADC) {
double Temp;
Temp = log((double) ((10240000/RawADC) - 10000) / 10000); // We divide by our thermistor's resistance at 25C, in this case 10K
Temp = 1 / (0.003354016 + (0.0002569850 * Temp) + (0.000002620131 * Temp * Temp) + (0.00000006383091 * Temp * Temp * Temp));
Temp = Temp - 273.15; // Convert Kelvin to Celsius
return Temp;
}
.
AVR Soft I2C Master
Use any pins to emulate I2C.
Using code from http://forums.adafruit.com/viewtopic.php?f=25&t=13722
#define TWI_SDA_PIN PB0
#define TWI_SCL_PIN PB3
#define EEPROM_ADDR 0x50
#define I2C_DELAY_USEC 4
#define I2C_READ 1
#define I2C_WRITE 0
// Initialize SCL/SDA pins and set the bus high
void SoftI2cMasterInit(void) {
DDRB |= (1<<TWI_SDA_PIN);
PORTB |= (1<<TWI_SDA_PIN);
DDRB |= (1<<TWI_SCL_PIN);
PORTB |= (1<<TWI_SCL_PIN);
}
// De-initialize SCL/SDA pins and set the bus low
void SoftI2cMasterDeInit(void) {
PORTB &= ~(1<<TWI_SDA_PIN);
DDRB &= ~(1<<TWI_SDA_PIN);
PORTB &= ~(1<<TWI_SCL_PIN);
DDRB &= ~(1<<TWI_SCL_PIN);
}
// Read a byte from I2C and send Ack if more reads follow else Nak to terminate read
uint8_t SoftI2cMasterRead(uint8_t last) {
uint8_t b = 0;
// Make sure pull-up enabled
PORTB |= (1<<TWI_SDA_PIN);
DDRB &= ~(1<<TWI_SDA_PIN);
// Read byte
for (uint8_t i = 0; i < 8; i++) {
// Don't change this loop unless you verify the change with a scope
b <<= 1;
_delay_us(I2C_DELAY_USEC);
PORTB |= (1<<TWI_SCL_PIN);
if (bit_is_set(PINB, TWI_SDA_PIN)) b |= 1;
PORTB &= ~(1<<TWI_SCL_PIN);
}
// Send Ack or Nak
DDRB |= (1<<TWI_SDA_PIN);
if (last) {
PORTB |= (1<<TWI_SDA_PIN);
}
else {
PORTB &= ~(1<<TWI_SDA_PIN);
}
PORTB |= (1<<TWI_SCL_PIN);
_delay_us(I2C_DELAY_USEC);
PORTB &= ~(1<<TWI_SCL_PIN);
PORTB &= ~(1<<TWI_SDA_PIN);
return b;
}
// Write a byte to I2C
bool SoftI2cMasterWrite(uint8_t data) {
// Write byte
for (uint8_t m = 0x80; m != 0; m >>= 1) {
// Don't change this loop unless you verify the change with a scope
if (m & data) {
PORTB |= (1<<TWI_SDA_PIN);
}
else {
PORTB &= ~(1<<TWI_SDA_PIN);
}
PORTB |= (1<<TWI_SCL_PIN);
_delay_us(I2C_DELAY_USEC);
PORTB &= ~(1<<TWI_SCL_PIN);
}
// get Ack or Nak
DDRB &= ~(1<<TWI_SDA_PIN);
// Enable pullup
PORTB |= (1<<TWI_SDA_PIN);
PORTB |= (1<<TWI_SCL_PIN);
uint8_t rtn = bit_is_set(PINB, TWI_SDA_PIN);
PORTB &= ~(1<<TWI_SCL_PIN);
DDRB |= (1<<TWI_SDA_PIN);
PORTB &= ~(1<<TWI_SDA_PIN);
return rtn == 0;
}
// Issue a start condition
bool SoftI2cMasterStart(uint8_t addressRW) {
PORTB &= ~(1<<TWI_SDA_PIN);
_delay_us(I2C_DELAY_USEC);
PORTB &= ~(1<<TWI_SCL_PIN);
return SoftI2cMasterWrite(addressRW);
}
// Issue a restart condition
bool SoftI2cMasterRestart(uint8_t addressRW) {
PORTB |= (1<<TWI_SDA_PIN);
PORTB |= (1<<TWI_SCL_PIN);
_delay_us(I2C_DELAY_USEC);
return SoftI2cMasterStart(addressRW);
}
// Issue a stop condition
void SoftI2cMasterStop(void) {
PORTB &= ~(1<<TWI_SDA_PIN);
_delay_us(I2C_DELAY_USEC);
PORTB |= (1<<TWI_SCL_PIN);
_delay_us(I2C_DELAY_USEC);
PORTB |= (1<<TWI_SDA_PIN);
_delay_us(I2C_DELAY_USEC);
}
// Read 1 byte from the EEPROM device and return it
uint8_t soft_i2c_eeprom_read_byte(uint8_t deviceAddr, uint16_t readAddress) {
uint8_t byteRead = 0;
// Issue a start condition, send device address and write direction bit
if (!SoftI2cMasterStart((deviceAddr<<1) | I2C_WRITE)) return false;
// Send the address to read, 8 bit or 16 bit
if (readAddress > 255) {
if (!SoftI2cMasterWrite((readAddress >> 8))) return false; // MSB
if (!SoftI2cMasterWrite((readAddress & 0xFF))) return false; // LSB
}
else {
if (!SoftI2cMasterWrite(readAddress)) return false; // 8 bit
}
// Issue a repeated start condition, send device address and read direction bit
if (!SoftI2cMasterRestart((deviceAddr<<1) | I2C_READ)) return false;
// Read the byte
byteRead = SoftI2cMasterRead(1);
// Issue a stop condition
SoftI2cMasterStop();
return byteRead;
}
// Write 1 byte to the EEPROM
bool soft_i2c_eeprom_write_byte(uint8_t deviceAddr, uint16_t writeAddress, byte writeByte) {
// Issue a start condition, send device address and write direction bit
if (!SoftI2cMasterStart((deviceAddr<<1) | I2C_WRITE)) return false;
// Send the address to write, 8 bit or 16 bit
if ( writeAddress > 255) {
if (!SoftI2cMasterWrite((writeAddress >> 8))) return false; // MSB
if (!SoftI2cMasterWrite((writeAddress & 0xFF))) return false; // LSB
}
else {
if (!SoftI2cMasterWrite(writeAddress)) return false; // 8 bit
}
// Write the byte
if (!SoftI2cMasterWrite(writeByte)) return false;
// Issue a stop condition
SoftI2cMasterStop();
// Delay 10ms for the write to complete, depends on the EEPROM you use
_delay_ms(10);
return true;
}
.
I2C EEPROM Size Tester
Test for a 8bit or 16bit EEPROM between 1Kbit to 512Kbit. Relies on AVR Soft I2C Master and MCU at 1MHz.
The global variable eepromMemsize is set with the EEPROM size, e.g if eepromMemsize equals 1, you do eepromMap[eepromMemsize] so the EEPROM found is 2Kbit.
Usage: eepromSizetest();
int eepromMemsize = -1; // No EEPROM found by default
#define eeprom1Kbit 127
#define eeprom2Kbit 255
#define eeprom4Kbit 511
#define eeprom8Kbit 1023
#define eeprom16Kbit 2047
#define eeprom32Kbit 4095
#define eeprom64Kbit 8191
#define eeprom128Kbit 16383
#define eeprom256Kbit 32767
#define eeprom512Kbit 65535
#define eepromA0 (1#define NOREPEATSTART 0
#define REPEATSTART 1
// Maps the EEPROM last writeable location
uint16_t eepromMap[10] = {
eeprom1Kbit,
eeprom2Kbit,
eeprom4Kbit,
eeprom8Kbit,
eeprom16Kbit,
eeprom32Kbit,
eeprom64Kbit,
eeprom128Kbit,
eeprom256Kbit,
eeprom512Kbit,
};
// Read 1 byte from the address without caring about the EEPROM size
// This function should not be used by anything other than eepromSizetest
uint8_t soft_i2c_eeprom_read_minimal(uint8_t deviceAddr, uint16_t readAddress) {
uint8_t byteRead = 0;
if (readAddress <= eeprom2Kbit) { // 1-2Kbit EEPROM
if (!SoftI2cMasterStart((deviceAddr<<1) | I2C_WRITE)) return false;
if (!SoftI2cMasterWrite(readAddress)) return false;
if (!SoftI2cMasterRestart((deviceAddr<<1) | I2C_READ)) return false;
}
else if (readAddress > eeprom2Kbit && readAddress <= eeprom4Kbit) { // Set A0 bit
if (!SoftI2cMasterStart(((deviceAddr<<1) | I2C_WRITE) | eepromA0)) return false;
if (!SoftI2cMasterWrite((readAddress & 0xFF))) return false;
if (!SoftI2cMasterRestart(((deviceAddr<<1) | I2C_READ) | eepromA0)) return false;
}
else if (readAddress > eeprom4Kbit && readAddress <= eeprom8Kbit) { // Set A1 bit
if (!SoftI2cMasterStart(((deviceAddr<<1) | I2C_WRITE) | eepromA1)) return false;
if (!SoftI2cMasterWrite((readAddress & 0xFF))) return false;
if (!SoftI2cMasterRestart(((deviceAddr<<1) | I2C_READ) | eepromA1)) return false;
}
else if (readAddress > eeprom8Kbit && readAddress <= eeprom16Kbit) { // Set A2 bit
if (!SoftI2cMasterStart(((deviceAddr<<1) | I2C_WRITE) | eepromA2)) return false;
if (!SoftI2cMasterWrite((readAddress & 0xFF))) return false;
if (!SoftI2cMasterRestart(((deviceAddr<<1) | I2C_READ) | eepromA2)) return false;
}
else { // 32Kbit+ EEPROM
if (!SoftI2cMasterStart((deviceAddr<<1) | I2C_WRITE)) return false;
if (!SoftI2cMasterWrite((readAddress >> 8))) return false; // MSB
if (!SoftI2cMasterWrite((readAddress & 0xFF))) return false; // LSB
if (!SoftI2cMasterRestart((deviceAddr<<1) | I2C_READ)) return false;
}
// Read the byte
byteRead = SoftI2cMasterRead(1);
// Issue a stop condition
SoftI2cMasterStop();
return byteRead;
}
// Write 1 byte to the address without caring about the EEPROM size
// This function should not be used by anything other than eepromSizetest
bool soft_i2c_eeprom_write_minimal(uint8_t deviceAddr, uint16_t writeAddress, byte writeByte) {
if (writeAddress <= eeprom2Kbit) { // 1-2Kbit EEPROM
if (!SoftI2cMasterStart((deviceAddr<<1) | I2C_WRITE)) return false;
if (!SoftI2cMasterWrite(writeAddress)) return false;
}
else if (writeAddress > eeprom2Kbit && writeAddress <= eeprom4Kbit) { // Set A0 bit
if (!SoftI2cMasterStart(((deviceAddr<<1) | I2C_WRITE) | eepromA0)) return false;
if (!SoftI2cMasterWrite((writeAddress & 0xFF))) return false;
}
else if (writeAddress > eeprom4Kbit && writeAddress <= eeprom8Kbit) { // Set A1 bit
if (!SoftI2cMasterStart(((deviceAddr<<1) | I2C_WRITE) | eepromA1)) return false;
if (!SoftI2cMasterWrite((writeAddress & 0xFF))) return false;
}
else if (writeAddress > eeprom8Kbit && writeAddress <= eeprom16Kbit) { // Set A2 bit
if (!SoftI2cMasterStart(((deviceAddr<<1) | I2C_WRITE) | eepromA2)) return false;
if (!SoftI2cMasterWrite((writeAddress & 0xFF))) return false;
}
else { // 32Kbit+ EEPROM
if (!SoftI2cMasterStart((deviceAddr<<1) | I2C_WRITE)) return false;
if (!SoftI2cMasterWrite((writeAddress >> 8))) return false; // MSB
if (!SoftI2cMasterWrite((writeAddress & 0xFF))) return false; // LSB
}
// Write the byte
if (!SoftI2cMasterWrite(writeByte)) return false;
// Issue a stop condition
SoftI2cMasterStop();
// Delay 10ms for the write to complete, depends on the EEPROM you use
_delay_us(10ms);
return true;
}
// Test what EEPROM which is inserted by writing and reading the last memory location of 1Kbit, 2Kbit, etc
// with different values, if the values don't match then we know the EEPROM memory limit has been reached
void eepromSizetest(void) {
int i = 9; // Start off checking the highest EEPROM (512Kbit)
// Check if it's an EEPROM with an 8bit address (if we don't check for this and run a write made for a 16bit
// addressable chip on an 8bit chip, it will write the second byte of the address to the first byte of the address)
SoftI2cMasterInit();
int saveByte = soft_i2c_eeprom_read_minimal(EEPROM_ADDR, eepromMap[0]);
soft_i2c_eeprom_write_minimal(EEPROM_ADDR, eepromMap[0], 1);
int readNumber = soft_i2c_eeprom_read_minimal(EEPROM_ADDR, eepromMap[0]);
soft_i2c_eeprom_write_minimal(EEPROM_ADDR, eepromMap[0], saveByte);
SoftI2cMasterDeInit();
if (readNumber == 1) {
i = 4; // Detected 8bit addressable EEPROM, start off at highest 8bit address EEPROM (16Kbit)
}
// Start checking for EEPROM size
while (i > 0) {
SoftI2cMasterInit();
// Save the data that is at the location we will write to
int saveByte = soft_i2c_eeprom_read_minimal(EEPROM_ADDR, eepromMap[i]);
int saveByte2 = soft_i2c_eeprom_read_minimal(EEPROM_ADDR, eepromMap[i-1]);
// Write/read test
soft_i2c_eeprom_write_minimal(EEPROM_ADDR, eepromMap[i], i);
soft_i2c_eeprom_write_minimal(EEPROM_ADDR, eepromMap[i-1], i-1);
int readNumber = soft_i2c_eeprom_read_minimal(EEPROM_ADDR, eepromMap[i]);
// Restore the data that was originally at the location
soft_i2c_eeprom_write_minimal(EEPROM_ADDR, eepromMap[i], saveByte);
soft_i2c_eeprom_write_minimal(EEPROM_ADDR, eepromMap[i-1], saveByte2);
// If we can still read the first value that we wrote to a higher location, we found the EEPROM size
if (readNumber == i) {
eepromMemsize = i;
break;
}
// When do a read/write test to the 32Kbit, we would actually do test read/write for a 16Kbit EEPROM (i-1) and that
// will cause problems because a 16Kbit EEPROM doesn't use 16 bit addresses. So on i == 6, we'll test if
// readNumber equals 5 which means we have a 32Kbit EEPROM as that's the lowest 16bit addressable EEPROM available.
if (i == 6 && readNumber == 5) {
eepromMemsize = 5;
break;
}
SoftI2cMasterDeInit();
i--;
}
// Try the smallest EEPROM size by itself if no other EEPROM size was found
if (eepromMemsize == -1) {
SoftI2cMasterInit();
int saveByte = soft_i2c_eeprom_read_minimal(EEPROM_ADDR, eepromMap[0]);
soft_i2c_eeprom_write_minimal(EEPROM_ADDR, eepromMap[0], 1);
int readNumber = soft_i2c_eeprom_read_minimal(EEPROM_ADDR, eepromMap[0]);
soft_i2c_eeprom_write_minimal(EEPROM_ADDR, eepromMap[0], saveByte);
if (readNumber == 1) {
SoftI2cMasterDeInit();
eepromMemsize = 0;
}
}
SoftI2cMasterDeInit();
}





Dear Alex,
It apprears that in
================
Code Snippets
Feb 25th, 2012 by Alex
The code snippets below are intended for Atmel AVR MCUs.
Watchdog Timer Delay function
=================
The snippet got mangled and is not the text you intend near “// Start timed sequence”
i.e…
/ Watchdog timeout values
// 0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms, 6=1sec, 7=2sec, 8=4sec, 9=8sec
// Used from http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/
void setup_watchdog (int ii) {
byte bb;
if (ii > 9 ) ii=9;
bb=ii & 7;
if (ii > 7) bb|= (1< bb|= (1<
MCUSR &= ~(1< // Start timed sequence
WDTCR |= (1< // Set new watchdog timeout value
WDTCR = bb;
WDTCR |= _BV(WDIE);
Thank you for the excellent examples…
Kent Smith
Thanks Kent, it’s now been updated.