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.