Author Topic: I2C AVR GCC Library  (Read 13227 times)

0 Members and 1 Guest are viewing this topic.

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
I2C AVR GCC Library
« on: September 12, 2013, 10:14:28 pm »
hello

i am building a library for my atmega328p its to allow all 4 communication methods over i2c (MT,MR,ST and SR)

now i think i have gotten the MT and MR modes done but i am unsure about the ST and SR for several reasons.

1, what do i have to initialise so that it acknowledges its own lave address? (do i just enable the TWEA bit in the TWCR register when i set its slave address?

2, what sort of interrupt is needed so that when i do receive data i can read it from the register and store it in a buffer

and i believe there are a few other problems as-well. i would have a look at an already made I2C library but i can only find MT and MR ones :/

here is the current library it allows initialisation and setting a slave address, sending the start, rep start and stop commands and allows for reading and writing data over the I2C bus i just need to get it so it is able to read and write in slave mode:
Code: [Select]
/*
 * Carbonwire.c
 *
 * Created: 12/09/2013 21:25:36
 *  Author: carbon dude oxide
 */

#include <avr/io.h>
#include <util/twi.h>

#include "Carbonwire.h"

static volatile uint8_t twi_state; //the current TWI state (MT,MS,RT,RS and ready)

int function(void)
{
    //TODO:: Please write your application code

    return 0;
}

void twi_begin(void) //initiate the TWI
{
twi_state = TWI_READY; //set the state as ready

PORTC = (1 << PORTC5) | (1 << PORTC4); //turn on internal pull ups on SDA and SCL lines

TWSR = 0; //ensure no prescaler
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2; //calculate TWI clock speed register (from data sheet)
}

void twi_setAddress(uint8_t address) //set a slave address for ATMega328
{
TWAR = (address << 1); //set the address in the upper bits and leave general call off (Right Most Bit)
// TWCR = (1 << TWEA) //enable slave ack
// TWCR = (1 << TWEN) | (1 << TWEA) | (1 << TWIE); //enable the TWI and allow interrupts and acks
}

uint8_t twi_start(uint8_t address) //send the address byte and
{

uint8_t twscb; //create TWI Status Check Byte

TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); //send start condition

while(!(TWCR & (1 << TWINT))); //wait until start transmission has completed

twscb = TWSR & 0xF8; // set the TWI Status Check Byte to bits 3-7 from TWSR
if ((twscb != TW_START) && (twscb != TW_REP_START)) return 1; //if the status is not start or repeated start return 1 (error)

TWDR = address; //load the address into TWDR
TWCR = (1 << TWINT) | (1 << TWEN); //enable TWI interrupts and send the address packet (via enabling)

while(!(TWCR & (1 << TWINT))); // wait until ack/nack has been received

twscb = TWSR & 0xF8; // set the TWI Status Check Byte to bits 3-7 from TWSR
if ((twscb != TW_MT_SLA_ACK) && (twscb != TW_MR_SLA_ACK) ) return 1; //if the status is not master transmit ack or master receive ack then return 1 (error)

switch (twscb)
{
case TW_MT_SLA_ACK: twi_state = TWI_MT; //set status to MT if in MT mode
case TW_MR_SLA_ACK: twi_state = TWI_MR; //set status to MR if in MR mode
}
return 0; //slave device has acknowledged and is ready to read/write data
}

uint8_t twi_repeatstart(uint8_t address)
{
return twi_start(address); //call the twi_start() function and return its value
}

uint8_t twi_write(uint8_t wdata)
{
if (twi_state != TWI_MT) return 1; //if the device is not in MT mode then it cannot write so return 1 (error)

uint8_t twscb; //create TWI Status Check Byte

TWDR = wdata; //load data into the register
TWCR = (1<<TWINT) | (1<<TWEN); //send the data packet stored in the register

while(!(TWCR & (1 << TWINT))); //wait until transmission has completed

twscb = TWSR & 0xF8; // set the TWI Status Check Byte to bits 3-7 from TWSR
if(twscb != TW_MT_DATA_ACK) return 1; //if the device has not returned an ack bit then return 1 (error)

return 0; //slave device has acknowledged the sent data packet
}

uint8_t twi_read(uint8_t ret) //read a byte from the slave device and return an ack/nack (ret value 1/0)
{
if (twi_state != TWI_MR) return 1; //if the device is not in MR mode then it cannot read so return 1 (error)

switch (ret)
{
case 0: TWCR = (1<<TWINT) | (1<<TWEN); //if ret is 0 send a nack byte back after reading
case 1: TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA); //if ret is 1 send a ack byte back after reading
}
while(!(TWCR & (1 << TWINT))); //wait for transmission to complete

return TWDR; // return the data that was just received from slave
}

void twi_stop(void)
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); //send the stop condition

while(TWCR & (1 << TWSTO)); // wait until top condition has been transmitted

twi_state = TWI_READY; //set internal status back to ready
}

-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
Re: I2C AVR GCC Library
« Reply #1 on: September 12, 2013, 10:20:52 pm »
ah, i have just found the application note for using it as an I2C slave.

if you know please still commend but i am going to have a look though the app note now :D
-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: I2C AVR GCC Library
« Reply #2 on: September 12, 2013, 10:36:36 pm »
Quote
1, what do i have to initialise so that it acknowledges its own lave address? (do i just enable the TWEA bit in the TWCR register when i set its slave address?

Set up the module as a receiver and specify its slave address. Enable interrupt and when a valid slave address is received from the bus, you jump to the isr.

Quote
2, what sort of interrupt is needed so that when i do receive data i can read it from the register and store it in a buffer

You will need to process the various status bits (based on what's being received) in the twi isr.

Read the datasheet or you stand no chance.
================================
https://dannyelectronics.wordpress.com/
 

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
Re: I2C AVR GCC Library
« Reply #3 on: September 12, 2013, 10:55:31 pm »
i have the data sheet out but i am having trouble finding the interupt for when the device receives a SLA+RW

im going to ask that at the bottom of the library can i call the ISR() directly? and is the vector for data TWI_vect? in the app note it calles the ISR like this:

Code: [Select]
#pragma vector=TWI_vect
__interrupt void TWI_ISR( void )
{

and then it uses a switch command to check all the status codes and what to do with them. i am unsure however how this calles the ISR with the right vector unfortunatly
-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: I2C AVR GCC Library
« Reply #4 on: September 12, 2013, 10:59:33 pm »
Quote
in the app note it calles the ISR like this:

If you have an IAR-AVR.

Otherwise, read the manual for your compiler.
================================
https://dannyelectronics.wordpress.com/
 

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
Re: I2C AVR GCC Library
« Reply #5 on: September 12, 2013, 11:17:36 pm »
kk il have a look now :) i believe im using GCC-AVR in atmel studio :)
-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline Kremmen

  • Super Contributor
  • ***
  • Posts: 1289
  • Country: fi
Re: I2C AVR GCC Library
« Reply #6 on: September 13, 2013, 07:00:59 am »
Then the interrupt vectors are listed in avr.libc documentation at http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
Nothing sings like a kilovolt.
Dr W. Bishop
 

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
Re: I2C AVR GCC Library
« Reply #7 on: September 13, 2013, 12:18:58 pm »
ok i think i have now finished the MT, MR and SR modes i am unsure about a couple of things (mainly error handling and making the code a bit more efficiant) here is the current code:

Code: [Select]
/*
 * CWire.c
 *
 * Created: 12/09/2013 21:25:36
 *  Author: carbon dude oxide
 */

#include <avr/io.h>
#include <avr/interrupt.h>

#include "CWire.h"

static uint8_t twi_state; //the current TWI state (MT,MS,RT,RS and ready)
static uint8_t twi_slave; //the current mode (0 for master, 1 for slave)

//receiving buffer
static uint8_t twi_rxbuffer[TWI_BUFFER_LENGTH]; //TWI rx buffer
static uint8_t twi_rxptr;
static uint8_t twi_rxbib; //bytes in buffer

//transmitting buffer
static uint8_t twi_txbuffer[TWI_BUFFER_LENGTH]; //TWI tx buffer
static uint8_t twi_txptr;
static uint8_t twi_txbib; //bytes in buffer

void twi_begin(void) //initiate the TWI
{
twi_flush(); //empty buffers (just in case)
twi_state = TWI_READY; //set the state as ready

PORTC = (1 << PORTC5) | (1 << PORTC4); //turn on internal pull ups on SDA and SCL lines

TWSR = 0; //ensure no prescaler
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2; //calculate TWI clock speed register (from data sheet)

TWCR = (1 << TWEN); //enable the I2C bus
twi_slave = 0; //set into master mode
}

void twi_setAddress(uint8_t address) //set a slave address for ATMega328 and set into slave mode
{
TWAR = (address << 1); //set the address in the upper bits and leave general call off (Right Most Bit)
TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (1 << TWEA); //enable TWI and interrupts and accept incoming save addresses.
twi_slave = 1; //set into slave mode
}

uint8_t twi_start(uint8_t address) //send the address byte and
{
twi_flush(); //empty buffers
uint8_t twscb; //create TWI Status Check Byte

TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN) | (0 << TWEA) | (0 << TWIE); //go to master mode and send start condition (disable slave ack if active)

while(!(TWCR & (1 << TWINT))); //wait until start transmission has completed

twscb = TWSR & 0xF8; // set the TWI Status Check Byte to bits 3-7 from TWSR
if ((twscb != TWI_START) && (twscb != TWI_REP_START)) return 1; //if the status is not start or repeated start return 1 (error)

TWDR = address; //load the address into TWDR
TWCR = (1 << TWINT) | (1 << TWEN); //enable TWI interrupts and send the address packet (via enabling)

while(!(TWCR & (1 << TWINT))); // wait until ack/nack has been received

twscb = TWSR & 0xF8; // set the TWI Status Check Byte to bits 3-7 from TWSR
if ((twscb != TWI_MT_ADR_ACK) && (twscb != TWI_MR_ADR_ACK) ) return 1; //if the status is not master transmit ack or master receive ack then return 1 (error)

switch (twscb)
{
case TWI_MT_ADR_ACK: twi_state = TWI_MT; //set status to MT if in MT mode
case TWI_MR_ADR_ACK: twi_state = TWI_MR; //set status to MR if in MR mode
}
return 0; //slave device has acknowledged and is ready to read/write data
}

uint8_t twi_repeatstart(uint8_t address) //send a repeated start command on the bus
{
return twi_start(address); //call the twi_start() function and return its value
}

uint8_t twi_write(uint8_t wdata) //write wdata to the bus as long as a slave has been ack
{
if (twi_state != TWI_MT) return 1; //if the device is not in MT mode then it cannot write so return 1 (error)

uint8_t twscb; //create TWI Status Check Byte

TWDR = wdata; //load data into the register
TWCR = (1<<TWINT) | (1<<TWEN); //send the data packet stored in the register

while(!(TWCR & (1 << TWINT))); //wait until transmission has completed

twscb = TWSR & 0xF8; // set the TWI Status Check Byte to bits 3-7 from TWSR
if(twscb != TWI_MT_DATA_ACK) return 1; //if the device has not returned an ack bit then return 1 (error)

return 0; //slave device has acknowledged the sent data packet
}

uint8_t twi_read(uint8_t ret) //read a byte from the slave device and return an ack/nack (ret value 1/0)
{
if (twi_state == TWI_MR) //if the device is in MR mode then read data from connected slave device
{
switch (ret)
{
case 0: TWCR = (1<<TWINT) | (1<<TWEN); //if ret is 0 send a nack byte back after reading
case 1: TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA); //if ret is 1 send a ack byte back after reading
}
while(!(TWCR & (1 << TWINT))); //wait for transmission to complete

return TWDR; // return the data that was just received from slave
}
else if ((twi_state == TWI_SR) || (twi_state == TWI_READY)) //if the device is in SR mode or Ready mode then read data from rx buffer
{
if (twi_rxbib > 0) //check to see if there are any bytes to read in the buffer
{
uint8_t retbuf = twi_rxbuffer[0]; // save the first received byte (first in first out)
for (int i=1; i<TWI_BUFFER_LENGTH;i++) //move items in the buffer over 1
{
twi_rxbuffer[i-1] = twi_rxbuffer[i];
}
twi_rxbuffer[((int)TWI_BUFFER_LENGTH)-1] = 0; //empty the last byte in the buffer

return retbuf; //return the saved buffer (oldest data / first to arrive byte)
}
else
{
return 1; //return an error as there are no byte to read in buffer
}
}
return 1; //if in any other mode return 1 (error)
}

void twi_stop(void)
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); //send the stop condition

while(TWCR & (1 << TWSTO)); // wait until top condition has been transmitted

twi_state = TWI_READY; //set internal status back to ready

switch (twi_slave)
{
case 0: TWCR = (1 << TWEN) | (0 << TWIE) | (0 << TWINT) | (0 << TWEA); //keep in master mode
case 1: TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (1 << TWEA); //set back to slave mode
}
}

void twi_flush(void) //empties all buffers and resets pointer and byte counter values
{
for (int i=0; i<TWI_BUFFER_LENGTH; i++) //repeat for length of buffers
{
twi_rxbuffer[i] = 0; //cycle though all buffer entries and reset to 0x00
twi_txbuffer[i] = 0; //cycle though all buffer entries and reset to 0x00
}
twi_rxbib = 0;
twi_txbib = 0;
twi_rxptr = 0;
twi_txptr = 0;

}

uint8_t twi_available(void) //returns amount of bytes contained within the rx buffer
{
return (uint8_t)twi_rxbib;
}

uint8_t twi_peak(void) //show most recent data without removing from the rx buffer
{
if (twi_rxbib == 0) return 0; // if there is no stored data then return

return twi_rxbuffer[(twi_rxptr-1)]; //return the latest stored data
}

ISR(TWI_vect)
{
switch(TWSR)
{
//ST mode statuses
case TWI_ST_ADR_ACK: //own SLA+R has been received and ACK has been sent. in ST mode
twi_state = TWI_ST;
case TWI_ST_ADR_ACK_M_ARB_LOST: // Arbitration lost in SLA+R/W as Master; own SLA+R has been received; ACK has been returned
case TWI_ST_DATA_ACK: // Data byte in TWDR has been transmitted; ack has been received
case TWI_ST_DATA_NACK: // Data byte in TWDR has been transmitted; nack has been received.
case TWI_ST_DATA_ACK_LAST_BYTE: // Last data byte in TWDR has been transmitted (TWEA = “0”); ACK has been received
//SR mode statuses
case TWI_SR_ADR_ACK: // Own SLA+W has been received ACK has been returned
twi_state = TWI_SR;
case TWI_SR_ADR_ACK_M_ARB_LOST: // Arbitration lost in SLA+R/W as Master; own SLA+W has been received; ACK has been returned
case TWI_SR_GEN_ACK: // General call address has been received; ACK has been returned
case TWI_SR_GEN_ACK_M_ARB_LOST: // Arbitration lost in SLA+R/W as Master; General call address has been received; ACK has been returned
case TWI_SR_ADR_DATA_ACK: // Previously addressed with own SLA+W; data has been received; ACK has been returned
twi_rxbuffer[twi_rxptr] = TWDR; //load the received data from the TWDR register into the rx buffer
twi_rxptr++; //add 1 to the pointer to prevent overwrites
twi_rxbib++; //add 1 to the mount of bytes in the buffer
case TWI_SR_ADR_DATA_NACK: // Previously addressed with own SLA+W; data has been received; NOT ACK has been returned
twi_rxbuffer[twi_rxptr] = TWDR; //load the received data from the TWDR register into the rx buffer
twi_rxptr++; //add 1 to the pointer to prevent overwrites
twi_rxbib++; //add 1 to the mount of bytes in the buffer
TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (1 << TWEA); //return to ready state, TWI, interrupt and ACK active
twi_state = TWI_READY;
case TWI_SR_GEN_DATA_ACK: // Previously addressed with general call; data has been received; ACK has been returned
case TWI_SR_GEN_DATA_NACK: // Previously addressed with general call; data has been received; NOT ACK has been returned
case TWI_SR_STOP_RESTART: // A STOP condition or repeated START condition has been received while still addressed as Slave
TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (1 << TWEA); //return to ready state, TWI, interrupt and ACK active
twi_state = TWI_READY;
//Miscellaneous statuses
case TWI_BUS_ERROR: // Bus error due to an illegal START or STOP condition
TWCR = (1 << TWSTO) | (1 << TWINT); //reset bus by releasing SDA and SCL lines
default: //no status code (0x00) or unknown status code
TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (1 << TWEA); //unknown status, return to ready state, TWI, interupt and ACK active
twi_state = TWI_READY;
}
}

The read function that i have has two different modes one for when the device is in MR mode and another when the device in in its ready state or MR mode which reads data from the rxbuffer (the slave buffer) (function is twi_read() ) the part that i would like to make more efficiant is when it removes data in the buffer then shuffles everything else along in it.

Code: [Select]
if (twi_rxbib > 0) //check to see if there are any bytes to read in the buffer
{
uint8_t retbuf = twi_rxbuffer[0]; // save the first received byte (first in first out)
for (int i=1; i<TWI_BUFFER_LENGTH;i++) //move items in the buffer over 1
{
twi_rxbuffer[i-1] = twi_rxbuffer[i];
}
twi_rxbuffer[((int)TWI_BUFFER_LENGTH)-1] = 0; //empty the last byte in the buffer

return retbuf; //return the saved buffer (oldest data / first to arrive byte)
}

so what this code currently does is saves the oldest data in the buffer (first byte that was received) and using a for loop moves everything along so rxbuffer[1] now becomes rxbuffer[0] and the same until the end of the buffer and then clears the last byte otherwise there would be a duplication now can I do this more efficiently as i am pretty sure there is a better way of doing this but i am unsure of what i would have to do.

another part I would like a bit of help on is when the bus status goes into its various arbitration modes t i just make the atmega release the bus pins? or do i have to do some other stuff as well? and how does the arbitration recovery process differer between the avr being a master and a slave as there are different status codes for a master arbitration and slave arbitration.

:D
-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline Kremmen

  • Super Contributor
  • ***
  • Posts: 1289
  • Country: fi
Re: I2C AVR GCC Library
« Reply #8 on: September 13, 2013, 03:11:49 pm »
Please do remember that in your long switch statement in the ISR, you need a break; after each case, otherwise control just falls through to the next case. That is almost never the intention. In other words the case{ does not delimit anything.
Nothing sings like a kilovolt.
Dr W. Bishop
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: I2C AVR GCC Library
« Reply #9 on: September 13, 2013, 03:50:13 pm »
It may be helpful to look into circular buffers - google linux kfifo.c.

Some chips, like stm32, allows the dma to configure a fifo buffer thus receiving can be done with minimum cpu care.

================================
https://dannyelectronics.wordpress.com/
 

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
Re: I2C AVR GCC Library
« Reply #10 on: September 13, 2013, 08:46:43 pm »
Please do remember that in your long switch statement in the ISR, you need a break; after each case, otherwise control just falls through to the next case. That is almost never the intention. In other words the case{ does not delimit anything.

thank you very much for letting me know :) just fixed that issue :)

It may be helpful to look into circular buffers - google linux kfifo.c.

Some chips, like stm32, allows the dma to configure a fifo buffer thus receiving can be done with minimum cpu care.



il have a look into it now :)

i have made a few alterations and have also added a new function for when arbitration is lost, it resets the device into its ready state and checks to see if it in master or slave mode. if its in slave mode it re enables the interupts and ack's for SLA+R/W

Code: [Select]
/*
 * CWire.c
 *
 * Created: 12/09/2013 21:25:36
 *  Author: carbon dude oxide
 */

#include <avr/io.h>
#include <avr/interrupt.h>

#include "CWire.h"

static uint8_t twi_state; //the current TWI state (MT,MS,RT,RS and ready)
static uint8_t twi_slave; //the current mode (0 for master, 1 for slave)

//receiving buffer
static uint8_t twi_rxbuffer[TWI_BUFFER_LENGTH]; //TWI rx buffer
static uint8_t twi_rxptr;
static uint8_t twi_rxbib; //bytes in buffer

//transmitting buffer
static uint8_t twi_txbuffer[TWI_BUFFER_LENGTH]; //TWI tx buffer
static uint8_t twi_txptr;
static uint8_t twi_txbib; //bytes in buffer

void twi_begin(void) //initiate the TWI
{
twi_flush(); //empty buffers (just in case)
twi_state = TWI_READY; //set the state as ready

PORTC = (1 << PORTC5) | (1 << PORTC4); //turn on internal pull ups on SDA and SCL lines

TWSR = 0; //ensure no prescaler
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2; //calculate TWI clock speed register (from data sheet)

TWCR = (1 << TWEN); //enable the I2C bus
twi_slave = 0; //set into master mode
}

void twi_setAddress(uint8_t address) //set a slave address for ATMega328 and set into slave mode
{
TWAR = (address << 1); //set the address in the upper bits and leave general call off (Right Most Bit)
TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (1 << TWEA); //enable TWI and interrupts and accept incoming save addresses.
twi_slave = 1; //set into slave mode
}

uint8_t twi_start(uint8_t address) //send the address byte and
{
twi_flush(); //empty buffers
uint8_t twscb; //create TWI Status Check Byte

TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN) | (0 << TWEA) | (0 << TWIE); //go to master mode and send start condition (disable slave ack if active)

while(!(TWCR & (1 << TWINT))); //wait until start transmission has completed

twscb = TWSR & 0xF8; // set the TWI Status Check Byte to bits 3-7 from TWSR
if ((twscb != TWI_START) && (twscb != TWI_REP_START)) return 1; //if the status is not start or repeated start return 1 (error)

TWDR = address; //load the address into TWDR
TWCR = (1 << TWINT) | (1 << TWEN); //enable TWI interrupts and send the address packet (via enabling)

while(!(TWCR & (1 << TWINT))); // wait until ack/nack has been received

twscb = TWSR & 0xF8; // set the TWI Status Check Byte to bits 3-7 from TWSR
if ((twscb != TWI_MT_ADR_ACK) && (twscb != TWI_MR_ADR_ACK) ) return 1; //if the status is not master transmit ack or master receive ack then return 1 (error)

switch (twscb)
{
case TWI_MT_ADR_ACK: twi_state = TWI_MT; //set status to MT if in MT mode
case TWI_MR_ADR_ACK: twi_state = TWI_MR; //set status to MR if in MR mode
}
return 0; //slave device has acknowledged and is ready to read/write data
}

uint8_t twi_repeatstart(uint8_t address) //send a repeated start command on the bus
{
return twi_start(address); //call the twi_start() function and return its value
}

uint8_t twi_write(uint8_t wdata) //write wdata to the bus as long as a slave has been ack
{
if (twi_state != TWI_MT) return 1; //if the device is not in MT mode then it cannot write so return 1 (error)

uint8_t twscb; //create TWI Status Check Byte

TWDR = wdata; //load data into the register
TWCR = (1<<TWINT) | (1<<TWEN); //send the data packet stored in the register

while(!(TWCR & (1 << TWINT))); //wait until transmission has completed

twscb = TWSR & 0xF8; // set the TWI Status Check Byte to bits 3-7 from TWSR
if(twscb != TWI_MT_DATA_ACK) return 1; //if the device has not returned an ack bit then return 1 (error)

return 0; //slave device has acknowledged the sent data packet
}

uint8_t twi_read(uint8_t ret) //read a byte from the slave device and return an ack/nack (ret value 1/0)
{
if (twi_state == TWI_MR) //if the device is in MR mode then read data from connected slave device
{
switch (ret)
{
case 0: TWCR = (1<<TWINT) | (1<<TWEN); //if ret is 0 send a nack byte back after reading
case 1: TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA); //if ret is 1 send a ack byte back after reading
}
while(!(TWCR & (1 << TWINT))); //wait for transmission to complete

return TWDR; // return the data that was just received from slave
}
else if ((twi_state == TWI_SR) || (twi_state == TWI_READY)) //if the device is in SR mode or Ready mode then read data from rx buffer
{
if (twi_rxbib == 0) return 1; //return an error as there are no bytes to read in buffer

uint8_t retbuf = twi_rxbuffer[0]; // save the first received byte (first in first out)
for (int i=1; i<TWI_BUFFER_LENGTH;i++) //move items in the buffer over 1
{
twi_rxbuffer[i-1] = twi_rxbuffer[i];
}
twi_rxbuffer[((int)TWI_BUFFER_LENGTH)-1] = 0; //empty the last byte in the buffer

return retbuf; //return the saved buffer (oldest data / first to arrive byte)
}
return 1; //if in any other mode return 1 (error)
}

void twi_stop(void)
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); //send the stop condition

while(TWCR & (1 << TWSTO)); // wait until top condition has been transmitted

twi_state = TWI_READY; //set internal status back to ready

switch (twi_slave)
{
case 0: TWCR = (1 << TWEN) | (0 << TWIE) | (0 << TWINT) | (0 << TWEA); //keep in master mode
case 1: TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (1 << TWEA); //set back to slave mode
}
}

void twi_flush(void) //empties all buffers and resets pointer and byte counter values
{
for (int i=0; i<TWI_BUFFER_LENGTH; i++) //repeat for length of buffers
{
twi_rxbuffer[i] = 0; //cycle though all buffer entries and reset to 0x00
twi_txbuffer[i] = 0; //cycle though all buffer entries and reset to 0x00
}
twi_rxbib = 0;
twi_txbib = 0;
twi_rxptr = 0;
twi_txptr = 0;

}

uint8_t twi_available(void) //returns amount of bytes contained within the rx buffer
{
return (uint8_t)twi_rxbib;
}

uint8_t twi_peak(void) //show most recent data without removing from the rx buffer
{
if (twi_rxbib == 0) return 0; // if there is no stored data then return

return twi_rxbuffer[(twi_rxptr-1)]; //return the latest stored data
}

void twi_arbitrationLost(void) //for use when arbitration occurs
{
switch (twi_slave) //check to see if slave has been set up, if it was re-enable to inter ups and ack's
{
case 0: TWCR = (1 << TWEN) | (0 << TWIE) | (0 << TWINT) | (0 << TWEA); //keep in master mode
case 1: TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (1 << TWEA); //set back to slave mode
}
twi_state = TWI_READY; //set internal status back to ready
}

ISR(TWI_vect)
{
switch(TWSR)
{
//Master status codes
case TWI_START: // START has been transmitted
break;
case TWI_REP_START: // Repeated START has been transmitted
break;
case TWI_ARB_LOST: // Arbitration lost
twi_arbitrationLost(); //drop out of the bus and return to ready state
break;
//MT status codes
case TWI_MT_ADR_ACK: // SLA+W has been transmitted and ACK received
break;
case TWI_MT_ADR_NACK: // SLA+W has been transmitted and NACK received
break;
case TWI_MT_DATA_ACK: // Data byte has been transmitted and ACK received
break;
case TWI_MT_DATA_NACK: // Data byte has been transmitted and NACK received
break;
//MRstatus codes
case TWI_MR_ADR_ACK: // SLA+R has been transmitted and ACK received
break;
case TWI_MR_ADR_ACK: // SLA+R has been transmitted and ACK received
break;
case TWI_MR_ADR_NACK: // SLA+R has been transmitted and NACK received
break;
case TWI_MR_DATA_ACK: // Data byte has been received and ACK transmitted
break;
case TWI_MR_DATA_NACK: // Data byte has been received and NACK transmitted
break;
//ST mode statuses
case TWI_ST_ADR_ACK: //own SLA+R has been received and ACK has been sent. in ST mode
twi_state = TWI_ST;
break;
case TWI_ST_ADR_ACK_M_ARB_LOST: // Arbitration lost in SLA+R/W as Master; own SLA+R has been received; ACK has been returned
break;
case TWI_ST_DATA_ACK: // Data byte in TWDR has been transmitted; ack has been received
break;
case TWI_ST_DATA_NACK: // Data byte in TWDR has been transmitted; nack has been received.
break;
case TWI_ST_DATA_ACK_LAST_BYTE: // Last data byte in TWDR has been transmitted (TWEA = “0”); ACK has been received
break;
//SR mode statuses
case TWI_SR_ADR_ACK: // Own SLA+W has been received ACK has been returned
twi_state = TWI_SR;
break;
case TWI_SR_ADR_ACK_M_ARB_LOST: // Arbitration lost in SLA+R/W as Master; own SLA+W has been received; ACK has been returned
break;
case TWI_SR_GEN_ACK: // General call address has been received; ACK has been returned
break;
case TWI_SR_GEN_ACK_M_ARB_LOST: // Arbitration lost in SLA+R/W as Master; General call address has been received; ACK has been returned
break;
case TWI_SR_ADR_DATA_ACK: // Previously addressed with own SLA+W; data has been received; ACK has been returned
twi_rxbuffer[twi_rxptr] = TWDR; //load the received data from the TWDR register into the rx buffer
twi_rxptr++; //add 1 to the pointer to prevent overwrites
twi_rxbib++; //add 1 to the mount of bytes in the buffer
break;
case TWI_SR_ADR_DATA_NACK: // Previously addressed with own SLA+W; data has been received; NOT ACK has been returned
twi_rxbuffer[twi_rxptr] = TWDR; //load the received data from the TWDR register into the rx buffer
twi_rxptr++; //add 1 to the pointer to prevent overwrites
twi_rxbib++; //add 1 to the mount of bytes in the buffer
twi_arbitrationLost(); //drop out of the bus and return to ready state (same process as loosing arbitration)
break;
case TWI_SR_GEN_DATA_ACK: // Previously addressed with general call; data has been received; ACK has been returned
break;
case TWI_SR_GEN_DATA_NACK: // Previously addressed with general call; data has been received; NOT ACK has been returned
break;
case TWI_SR_STOP_RESTART: // A STOP condition or repeated START condition has been received while still addressed as Slave
twi_arbitrationLost(); //drop out of the bus and return to ready state (same process as loosing arbitration)
break;
//Miscellaneous statuses
case TWI_BUS_ERROR: // Bus error due to an illegal START or STOP condition
TWCR = (1 << TWSTO) | (1 << TWINT); //reset bus by releasing SDA and SCL lines
break;
default: //no status code (0x00) or unknown status code
twi_arbitrationLost(); //drop out of the bus and return to ready state (same process as loosing arbitration)
break;
}
}

-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
Re: I2C AVR GCC Library
« Reply #11 on: September 13, 2013, 09:20:51 pm »
just a question, is there a way to include C++ like classes in the C code. an example of this is the arduino Wire library where inside the main header file is a class function where it lists all of the higher/public functions that the user can use now i know that class is a C++ and the header files in avr studio are compiled using C is there either:

a way of getting the header file to be built in C++ so that i can make use of the class functions (to be able to call functions from the main code like: CarbonWire.begin() )
or is there a C code equivalent?

:D i thinks its a nice idea and i helps the code by making it easer to see where that function is called and it hides the nitty gritty stuff within the library :D
-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline Kremmen

  • Super Contributor
  • ***
  • Posts: 1289
  • Country: fi
Re: I2C AVR GCC Library
« Reply #12 on: September 14, 2013, 06:05:41 am »
just a question, is there a way to include C++ like classes in the C code.
Err... no. They came up with C++ to do just that, you know.
Quote
an example of this is the arduino Wire library where inside the main header file is a class function where it lists all of the higher/public functions that the user can use now i know that class is a C++ and the header files in avr studio are compiled using C is there either:

a way of getting the header file to be built in C++ so that i can make use of the class functions (to be able to call functions from the main code like: CarbonWire.begin() )
or is there a C code equivalent?
What is preventing you from turning your project into a C++ project? Or let me answer that: there is nothing preventing you from turning your project into a C++ one, only yourself. The toolchain will do it no questions asked.
Quote
:D i thinks its a nice idea and i helps the code by making it easer to see where that function is called and it hides the nitty gritty stuff within the library :D
And so did Stroustrup &co when they came up with C++  ;D
Nothing sings like a kilovolt.
Dr W. Bishop
 

Offline carbon dude oxideTopic starter

  • Frequent Contributor
  • **
  • Posts: 429
  • Country: gb
Re: I2C AVR GCC Library
« Reply #13 on: September 14, 2013, 07:09:21 am »
Would i have to convert the main code into c++ aswell or can i have a seperate .h and .cpp file and have it linked to the original .h file inside an extern "C" {
#include "originalC.h"
}
Or do i have to convert it into c++ aswell?
-----
Everything Should Be Made as Simple as Possible, But Not Simpler
-----
 

Offline Kremmen

  • Super Contributor
  • ***
  • Posts: 1289
  • Country: fi
Re: I2C AVR GCC Library
« Reply #14 on: September 14, 2013, 07:37:21 am »
Would i have to convert the main code into c++ aswell or can i have a seperate .h and .cpp file and have it linked to the original .h file inside an extern "C" {
#include "originalC.h"
}
Or do i have to convert it into c++ aswell?
In your original post you say this:

hello

i am building a library for my atmega328p its to allow all 4 communication methods over i2c (MT,MR,ST and SR)


so obviously, if the library is in fact a C++ class, then it is not callable from C, only C++. Yes, your main program needs to be _formally_ a C++ application. I emphasize formally, because other than being a C++ source file, it can remain essentially as is. Due to the difference in calling conventions, if you mix C and C++, you may need to guide the compiler with some extern "C" directives but other than that, no big deal.
As a rule, i try to create fully self contained applications, i.e. all the code that goes into the app is part of the project source repository. Other than compiler standard libraries, that is. That way it can all be compiled as C++ and you don't need to worry about extern "C"s because there is none. The actual source code can still be vanilla C, ignoring some miniscule details, the compiler won't mind.

You would then have your main() in a main.cpp file (or whatever you call it), and your i2c class in a i2c.h header and i2c.cpp source file. Include normally where needed and you are basically done. That is absolutely the way i would do it, and in fact am doing it for many peripherals. Doing this consistently will result in very "Arduino-like  ::)", easily used peripheral classes hiding all the dirty details from the main code. You just specify during instance construction (or initialization if you don't care for dynamic instances) how you wish the object to behave this time.

Nothing sings like a kilovolt.
Dr W. Bishop
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf