Feed on
Posts
Comments

Code Snippets

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();
}

2 Responses to “Code Snippets”

  1. Kent Smith says:

    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