[tutorial] interrupt driven twi interface for avr (part 1_ mt_mr) _ chrisherring
TRANSCRIPT
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
1/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/
ChrisHerring.netGraduate Engineer Projects
[TUTORIAL] Interrupt Driven TWI Interface forAVR (Part 1: MT/MR)
Hello Everyone, I bring to you a tutorial on how the AVR TWI module operates. This is part 1 of the
tutorial and deals with Master Transmitter and Master Receiver mode. Stay tuned for part 2 which will
build on this to add Slave Receiver and Slave Transmitter functionality.
I2C (Inter Integrated Circuit) or TWI (Two Wire Interface) is a half duplex serial two wire interface for
interconnecting low to medium speed peripherals. It uses two open drain signal lines, Serial Data (SDA)
and Serial Clock (SCL). As these are open drain lines the device may sink however it cannot drive a line
high. In order to allow for high signals the lines must be pulled high using a pull-up resistor. The bus
drivers of all TWI compliant devices are open drain and this is essential to the operation of the interface.
TWI is a master/slave protocol. Multiple masters are allowed however only one device may be master at
any one time.
The ATMEGA and ATTINY range of ATMEL MCU’s (along with the majority of other ATMEL MCU’s) have a
dedicated TWI bus interface which takes care of START/STOP conditions, SCL clock, arbitration and
address detection. They also have dedicated shift registers for sending and receiving data on the bus.
The TWI pins also have slew rate limiting and a spike detection to remove spikes shorter than 50ns. The
internal pull ups can be enabled by setting the PORT bits on the SDA and SCL pins to high.
I will 觡rst provide an overview of the TWI interface, followed by an overview of the AVR TWI hardware
bef ore I get into designing the interrupt driven TWI library.
The Code:
If you just want the code to use, download it here:
TWIlib.c
http://www.chrisherring.net/wp-content/uploads/2014/03/TWIlib.chttp://www.chrisherring.net/http://www.chrisherring.net/http://www.chrisherring.net/wp-content/uploads/2014/03/TWIlib.chttp://www.chrisherring.net/
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
2/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 2
TWIlib.h
Basic Transmission of Bits
Each data bit over the line is transmitted during a high SCL pulse, the bit should be stable on the data
line BEFORE the SCL line is pulsed high and may only change AFTER the SCL line goes low. Thus the data
line must remain static for the duration that the SCL line is HIGH, the exception to this rule is when
generating a START or STOP condition as explained in the proceeding section.
Data bit transmission during CLK HIGH pulse – Image from ATMEGA datasheet
A TWI packet is 9 bits long; a data packet consists of one 8-bit data byte and one acknowledgement bit;
an address packet consists of one 7-bit address, one R/W bit and one acknowledgment bit.
The clock rate is set by the Master , however handshaking between Master and Slave can be achieved
using the open drain bus design. The Slave may extend the duration of the clock LOW period by holding
the SCL line LOW if it requires more time for processing or if the Master clock frequency is too high.
Start/Stop Condition
When a Master would like to initiate a transfer and take control of the bus, it transmits a START
condition. A START condition is transmitted by transitioning SDA from HIGH to LOW whilst the clock line
is HIGH. When the transmission is 觡nished and the Master would like to release control of the bus, a
STOP condition is transmitted. A STOP condition is transmitted by transitioning SDA from LOW to HIGH
whilst the clock line is HIGH. Between the START and STOP conditions, the bus is considered busy and
no other Master should attempt to take control. If a Master would like to begin another transmission
without relinquishing control of the bus (ie write a read address to a device and then perform a read
http://www.chrisherring.net/wp-content/uploads/2014/03/TWIlib.h
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
3/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 3
operation), it can send a REPEATED START condition. This is a START condition sent between another
START and a STOP.
The START, STOP and REPEATED START conditions – Image from ATMEGA datasheet
The ACK and NACK Conditions
The 觡nal condition on the line to cover is the ACK and NACK bits. These are transmitted by the receiver
during the ninth bit of the data packet. In order to transmit an ACK (acknowledged) bit, the receiver
pulls the SDA line LOW for the ninth bit. In order to transmit the NACK (not acknowledged) bit, the
receiver does not pull the SDA line low (hence it is pulled high via the pull up resistors). This allows a
NACK bit to be transmitted intentionally by the receiver, or if the receiver stops
responding/communication is severed it is also transmitted.
Addressing a Slave
A TWI device is addressed using a 7-bit data bit. Hence a total of 127 devices may be connected to a TWI
bus, where the address 000 0000 is reserved for a broadcast to all slaves. When a start condition is
detected, a slave will begin listening for an address, if the slave recognises the address being
transmitted as its own then it will pull the data line low during the ninth clock cycle in order to
acknowledge that it is listening (send the ACK). Following the address bits, a read /write bit is
transmitted, this indicates if the slave should be set up to receive data (write operation, R/W = 0) or send
data over the bus (read operation R/W = 1).
Some Terminology:
Slave address + WRITE bit SLA+W
Slave address + READ bit SLA+R
http://www.chrisherring.net/wp-content/uploads/2014/03/STARTSTOP.png
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
4/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 4
The structure of the address packet
Overview of AVR TWI Registers
Before I get into detail about setting up the TWI and designing the interrupt driven library, it is prudent
to 觡rst give an overview of the TWI registers and the meaning of the the bits that I will be referring to
later. For more detailed information obout these registers and the bits therein, head to the ATMEL
website and download the datasheet for your device. The information I present here can be found in
these datasheet, I just attempt to present in a concise and cogent manner. There are six 8-bit registers
associated with the TWI, they are;
TWBR: TWI Bit Rate Register
bit: 7 6 5 4 3 2 1 0
TWBR7 TWBR6 TWBR5 TWBR4 TWBR3 TWBR2 TWBR1 TWBR0
The bit rate register allows a division factor to be set for the TWI clock. This division factor divides
the CPU clock in order to set the SCL frequency according to the following equation:
SCL clock frequency calculation
Where TWPS is a prescale bit (see the TWI status register TWSR)
TWSR: TWI Status Register
http://www.chrisherring.net/wp-content/uploads/2014/03/TWBRcalc.pnghttp://www.atmel.com/products/microcontrollers/avr/default.aspxhttp://www.chrisherring.net/wp-content/uploads/2014/03/ADDRESSPACKET.png
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
5/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 5
bit: 7 6 5 4 3 2 1 0
TWS7 TWS6 TWS5 TWS4 TWS3 - TWPS1 TWPS0
The TWI status register – as its name implies – contains the status of the TWI hardware along with
the two prescaler bits which when combined with the TWBR register value are used to calculate the
SCL clock frequency.
Bits 3:7 are the status code bits. As the register also contains prescale bits, a mask should be used
when reading the status code.
Bits 0:1 are the prescaler bits. The prescale value is a two bit binary number, giving it a value
between 0 and 4. This prescale value is the exponent to a base of 4 in the SCL frequency
calculation. Hence these prescale bits can be used to generate values of:
TWPS0 TWPS1 Prescaler Value
0 0 1
0 1 4
1 0 16
1 1 64
Bit 2 is read only and had an initial value of 0 – hence is always 0.
TWCR: TWI Control Register
bit: 7 6 5 4 3 2 1 0
TWINT TWEA TWSTA TWSTO TWWC TWEN - TWIE
The TWI Control Register – again as its name implies – is used to control the TWI. It contains control bits
to generate the start and stop conditions, enable interrupts
Bit 7: TWINT is the TWI Interrupt ag. This bit is set HIGH when the TWI module has 觡nished
1 status = TWBR & 0xF8
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
6/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 6
working in the background and expects a response from the software. As long as the TWINT bit is
set the SCL line will be held low. This allows the software to ensure that the data is processed
before the next data bit is sent/received. The TWINT ag must be cleared by software by writing a logic
1 to it . If interrupts are enabled and the TWIE ag is also set, then the MCU will jump to the TWI
interrupt vector when TWINT gets set.
Bit 6: TWEA controls if the MCU should respond with an ACK under the following conditions:
1. A data byte has been received.
2. The device is addressed by a master.
3. The device detects a broadcast address (000 0000).
If the TWEA bit is not set then the device will not transmit any ACK bits hence it is essentially
disabled.
Bit 5: TWSTA is written to logic 1 when it is desired to transmit a START condition. If the bus is not
currently free, the TWI module will wait until a STOP is detected before transmitting the START and
taking control of the bus. After the START condition has been sent, the TWSTA ag should be
cleared by software.
Bit 4: TWSTO is written to a logic 1 when it is desired to send a STOP condition. Unlike the
START condition, the TWSTO ag will be cleared after the STOP condition is sent.
Bit 3: TWWC is the Write Collision ag. If an attempt is made to write to TWDR when TWINT is highthen this ag will be set to indicate that it is an illegal write, TWDR is set to read mode.
Bit 2: TWEN is the TWI enable bit, when this bit is set the TWI hardware takes control of the SCL
and SDA pins. Writing this bit to logic 0 disables the TWI and terminates any operations
immediately.
Bit 1: unused, Has an initial value of 0 and is read only – Is always 0.
Bit 0: TWIE is the TWI interrupt enable ag. Provided that global interrupts are enabled, setting this
bit will cause the MCU to jump to the TWI interrupt vector when the TWINT ag is set.
TWDR: TWI Data Register
bit: 7 6 5 4 3 2 1 0
TWD7 TWD5 TWD5 TWD4 TWD3 TWD2 TWD1 TWD0
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
7/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 7
This is the sift register for sending and receiving data. In receive mode, when data is available it can be
read from this register. When in transmit mode, a data byte is loaded into this register to be shifted
along the data line.
Modes of operation
There are four modes of operation that a TWI device can be in, they are;
MT: Master Transmitter
Device sends a START and addresses a slave with a write operation. Slave becomes a Slave
Receiver and a number of bytes are transferred to it.
MR: Master Receiver
Device sends a START and addresses a slave with a read operation. Slave becomes a Slave
Transmitter and a number of bytes are received from it.
ST: Slave Transmitter
Device detects a START followed by its own addresses with a read operation. Device
becomes a Slave Transmitter and a number of bytes are sent.
SR: Slave Receiver
Device detects a START followed by its own addresses with a write operation. Device
becomes a Slave Receiver and a number of bytes are received.
Initializing TWI
Before TWI can be used some initialization is required such as setting up the clock frequency, enabling
the module and setting up some variables.
Application Variables: A struct is used to hold the status of the TWI software and its current operating
mode:
12345678910111213141516
typedef enum { Ready, Initializing, RepeatedStartSent, MasterTransmitter, MasterReceiver, SlaceTransmitter, SlaveReciever } TWIMode; typedef struct TWIInfoStruct{ TWIMode mode; uint8_t errorCode; uint8_t repStart;
}TWIInfoStruct;TWIInfoStruct TWIInfo;
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
8/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 8
TWIinfo.mode is an enumerated type indicating the current state operating mode of the interface. This
mode will be checked to ensure that the mode is ready before initiating transfers.
TWIinfo.errorCode holds the error code, determined form the status register. When a transfer is
successful this is set to 0xFF. There are no possible error codes that can take the value of 0xFF even if
the TWSR is masked wrong, this is because the reserved bit 2 is zero and read only.
TWIinfo.repStart is a boolean indicating if a repeated start should be sent. If this is one then a Repeated
START will be sent, otherwise a STOP will be sent at the end of the transfer.
SCL frequency: As mentioned previously, when Master the SCL frequency is generated by dividing the
cpu frequency according to the equation:
SCL clock frequency calculation
It is more convenient to specify a desired SCL frequency and calculate TWBR by re-arranging the
equation.
Enabling TWI module and Interrupts: The Global interrupt bit should already be set by the application,
without this, no interrupts are enabled. In order to enable the TWI module the TWEN bit or the TWCR
register should be set, In order to enable the TWI interrupt vector, write the TWIE bit of TWCR to 1.
TWI is now enabled and ready to go!
Global Application Variables
There are several global variables de觡ned in the header;
123456789
101112
void TWIInit(){ TWIInfo.mode = Ready; TWIInfo.errorCode = 0xFF; TWIInfo.repStart = 0; // Set pre-scalers (no pre-scaling) TWSR = 0; // Set bit rate TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;
// Enable TWI and interrupt TWCR = (1
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
9/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 9
Master Transmitter:
A transmit buꙸer is set up to hold data to be transmitted. The buꙸer index and buꙸer length are used
to move through this buꙸer sending one byte at a time.
Master Receiver:
A receive buꙸer is set up in the same way as the transmit buꙸer.
TWI Control Macros
Some macros are also set up to control the TWI hardware and set the control register TWCR
appropriately:
TWISendStart() : Send the START signal, enable interrupts and TWI, clear TWINT ag to resume
transfer.
TWISendStop() : Send the STOP signal, enable interrupts and TWI, clear TWINT ag.
TWISendTransmit() : Used to resume a transfer, clear TWINT and ensure that TWI and interrupts
are enabled.
TWISendACK() : FOR MR mode. Resume a transfer, ensure that TWI and interrupts are enabled
and respond with an ACK if the device is addressed as a slave or after it receives a byte.
TWISendNACK() : FOR MR mode. Resume a transfer, ensure that TWI and interrupts are enabled
but DO NOT respond with an ACK if the device is addressed as a slave or after it receives a byte.
Master Transmitter
1234567
// Maximum Transmit buffer length#define TXMAXBUFLEN 14// Global transmit bufferuint8_t TWITransmitBuffer[TXMAXBUFLEN];// Transmit buffer indexvolatile int TXBuffIndex;int TXBuffLen; // The total length of the transmit buffer
1234
56
// Receive buffer length#define RXMAXBUFLEN 14// Global receive buffervolatile uint8_t TWIReceiveBuffer[RXMAXBUFLEN];
int RXBuffIndex; // Current index in the receive bufferint RXBuffLen; // The total number of bytes to read (should be less than RXMAXBUFFLEN)
12345
#define TWISendStart() (TWCR = (1
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
10/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 10
Master Transmitter is used when a device would like to initiate a transfer and send data to a slave, It is
the simplest and the code can be re-used, so I will start here and build onto it later.
All status codes that I refer to are the upper 5 bits of the TWSR status register ONLY. As the
lower three bits of TWSR are prescale and reserved bit. Status code = TWSR & 0xF8.
The steps involved here are;
1. Populate transmit buꙸer and set up variables. More detail below.
2. Send START condition in order to become Master and take control of the bus. This is done by
setting the TWSTA bit in the TWCR register. If the line is already busy, the TWI module will wait
until a STOP condition is issued and the line is free before sending. If in Repeated START mode,
then the START does not need to be sent.
3. Ensure that TWI module and interrupts are enabled. This is done by setting the TWEN and TWIE
bits of the TWCR register.
4. Clear the TWINT bit of TWCR in order to initiate transfer.
5. When START condition has been sent, the MCU will be be sent to the TWI interrupt vector and the
status register TWSR will have a value of 0x08 in its upper 5 bits.
6. Load the address byte with write bit into the data register TWDR and clear the TWINT bit to
continue transfer.
7. After the address byte and write bit have been transferred, there are three possible states;
Transmission success: A slave identi觡es its address and holds the data line low in the 9th
clock cycle (sends an ACK). Status code: 0x18
Transmission failed: No ACK is received in the 9th clock cycle. Either there is no slave with
that address, the slave is busy/intentionally not responding, TWI lines are damaged. I this
case it is up to the application software if the transmission should be retired or aborted. To
continue, send a STOP/START or Repeated START and try again. Status code: 0x20
Transmission failed: Arbitration has been lost – this will be discussed later and is beyond the
scope at this point. Status code: 0x38
8. Assuming the address byte was successfully transferred, the transmission can be continued. The
next data byte is placed in TWDR and the TWINT ag is cleared to continue transfer.
9. After the data byte has been transmitted, there are the same three possible states;
Transmission success: Salve acknowledges data bye. Status code: 0x28Transmission failed: No ACK is received in response to data byte. Status code: 0x30
Transmission failed: Arbitration has been lost. Status code: 0x38
10. Step 8 and 9 are continued for all data bytes in the transmit buꙸer. Once transmission is
complete, the Master can send a STOP signal to release control of the bus OR can send a
Repeated START to 觡nish the transmission however retain control of the bus for the next
transmission. If a Repeated START is sent then the TWI mode will be set to RepeatedStart. This will
be checked upon beginning the next transmission, if it has been sent then the next transmission
will need to manually add the 觡rst byte to the TWDR as there will be no interrupt vector.
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
11/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 1
Let’s take a look at each of these steps in a little more detail and with some code:
First take a look at the transmit data function prototype:
Here, we pass in a void pointer to an array of bytes to send, the length of the array and a boolean value
indicating if a STOP or a Repeated Start should be sent after the transfer. (1 – send the RS, 0 – send aSTOP)
A boolean is returned to indicate if the data that was passed to it is valid or not (ie. is the length of the
data short enough to 觡t in the global transmit buꙸer).
Step 1: First we wait until the TWI application is ready, then must transfer all of the data that TXdata
points to into the global transmit buꙸer. dataLen tells how many bytes are to be transmitted. The global
transmit buꙸer index should then be reset and the repeated start ag should be set appropriately:
SLA+R/W AND Data is stored in the one transmit buꙸer. Data is now ready to be sent over the TWI bus.
Step 2, 3, 4: These three steps can be combined into the one step. If a Repeated START has not been
sent then send the START signal. Once the start signal has been successfully sent then the MCU will
jump to the interrupt vector with status code 0x08 where the 觡rst byte is loaded into the TWDR. The
START is sent using a macro TWISendStart().
If however a Repeated START has already been sent previously, then the interrupt vector will have
already been triggered and the TWI mode will have been set to RepeatedStartSent. if this is the case then
we must 觡rst load the 觡rst byte into TWDR manually before resuming the transmission using the
TWISendTransmit() macro.
1 uint8_t TWITransmitData(void *const TXdata, uint8_t dataLen, uint8_t repStart)
12345678910111213
// Wait until ready while (!isTWIReady()) {_delay_us(1);} // Set repeated start mode TWIInfo.repStart = repStart; // Copy data into the transmit buffer uint8_t *data = (uint8_t *)TXdata; for (int i = 0; i < dataLen; i++) { TWITransmitBuffer[i] = data[i]; } // Copy transmit info to global variables TXBuffLen = dataLen; TXBuffIndex = 0;
1234
// If a repeated start has been sent, then devices are already listening for// and another start does not need to be sent.if (TWIInfo.mode == RepeatedStartSent)
{
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
12/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 12
At this point we have looked at the entire transmit function – easy! Just wrap it in a condition to check if
the data will 觡t in the buꙸer or not:
The isTWIReady() function checks if the TWI is in Ready or RepeatedStartSent mode and returns 1,
otherwise 0:
5678910111213
TWIInfo.mode = Initializing; TWDR = TWITransmitBuffer[TXBuffIndex++]; // Load data to transmit buffer TWISendTransmit(); // Send the data } else // Otherwise, just send the normal start signal to begin transmission. { TWIInfo.mode = Initializing; TWISendStart(); }
12345678
9101112131415161718192021
222324252627282930313233
343536373839
uint8_t TWITransmitData(void *const TXdata, uint8_t dataLen, uint8_t repStart){ if (dataLen
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
13/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 13
From here and onward, we jump to the interrupt vector and work there. A quick overview of the simple
structure of this interrupt vector is necessary. The TWI_STATUS macro is used to obtain the value of the
status register TWSR and mask it with 0xF8 to get the status. This status is then switched and the action
to take is placed in the corresponding case statement. An important note for those that do not know
exactly how the switch-case works – the switch will jump to the matching case, however will continue
through all of the cases from that point until a break;. Some macros are written for each status code to
increase readability;
Step 5, 6: These two steps apply only to the case in which a START is sent, not a Repeated START, you
will notice that loading the data into the TWDR and calling TWISendTransmit(); is the same as the
Repeated START condition in the transmit function.
Step 7/8 (success): After an address byte + write bit is successfully sent, the interrupt vector is once
again entered with the status code 0x18 or TWI_MT_SLAW_ACK as de觡ned. The next data bit should be
loaded into the TWDR and TWISendTransmit() is called again to resume the transmission. In fact, the only
diꙸerence between what is required from this step and the previous step is that the status code
indicates that we have successfully addressed a slave and have entered in to Master Transmitter mode.
Hence the same code can be used.
Step 9 (success): After a data byte has been successfully transmitted, we are back in the interrupt vector
and the next data byte should be placed in TWDR and transmission resumed. This step is once again
identical to Step 5, 6. The same code is used.
34567891011
if ( (TWIInfo.mode == Ready) | (TWIInfo.mode == RepeatedStartSent) ) { return 1; } else { return 0; }}
1234567891011
1213
// TWI Status Codes#define TWI_START_SENT 0x08 // Start sent#define TWI_REP_START_SENT 0x10 // Repeated Start sent// Master Transmitter Mode#define TWI_MT_SLAW_ACK 0x18 // SLA+W sent and ACK received#define TWI_MT_SLAW_NACK 0x20 // SLA+W sent and NACK received#define TWI_MT_DATA_ACK 0x28 // DATA sent and ACK received#define TWI_MT_DATA_NACK 0x30 // DATA sent and NACK received// Miscellaneous States#define TWI_LOST_ARBIT 0x38 // Arbitration has been lost#define TWI_NO_RELEVANT_INFO 0xF8 // No relevant information available
#define TWI_ILLEGAL_START_STOP 0x00 // Illegal START or STOP condition has been detec#define TWI_SUCCESS 0xFF // Successful transfer, this state is impossible from TWSR a
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
14/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 14
Step 10: Data bytes are continued to be transferred until one fails or all data bytes have been
transmitted. As the code for the previous three steps are identical, simply add a condition to check if
there are more bytes to send at each ISR call and the same code is valid for all steps whilst also dealing
with STOP/Repeated START at the conclusion of the transmission. The full interrupt vector code for
these steps:
Finally, the code must deal with non successful transfers. There were three possible error codes to deal
with;
TWI_MT_SLAW_NACK (0x20): Salve address and write bit was transferred, NACK received.
TWI_MT_DATA_NACK (0x30): Data was transferred, NACK received.
TWI_LOST_ARBIT (0x38): Arbitration lost.
All of these are handled in the same manner; return the error, set the mode of the TWI application to be
able to start another transfer; send a STOP or Repeated Start.
1
234567891011121314151617181920212223242526
272829303132
switch (TWI_STATUS)
{ // ----\/ ---- MASTER TRANSMITTER OR WRITING ADDRESS ----\/ ---- // case TWI_MT_SLAW_ACK: // SLA+W transmitted and ACK received // Set mode to Master Transmitter TWIInfo.mode = MasterTransmitter; case TWI_START_SENT: // Start condition has been transmitted case TWI_MT_DATA_ACK: // Data byte has been transmitted, ACK received if (TXBuffIndex < TXBuffLen) // If there is more data to send { TWDR = TWITransmitBuffer[TXBuffIndex++]; // Load data to transmit buf TWIInfo.errorCode = TWI_NO_RELEVANT_INFO; TWISendTransmit(); // Send the data } // This transmission is complete however do not release bus yet else if (TWIInfo.repStart) { TWIInfo.errorCode = 0xFF; TWISendStart(); } // All transmissions are complete, exit else { TWIInfo.mode = Ready; TWIInfo.errorCode = 0xFF; TWISendStop();
} break; case TWI_REP_START_SENT: // Repeated start has been transmitted // Set the mode but DO NOT clear TWINT as the next data is not yet ready TWIInfo.mode = RepeatedStartSent; break;
1234
case TWI_MT_SLAW_NACK: // SLA+W transmitted, NACK received case TWI_MT_DATA_NACK: // Data byte has been transmitted, NACK received case TWI_LOST_ARBIT: // Arbitration has been lost // Return error and send stop and set mode to ready
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
15/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 15
The transmit functionality is now complete.
Master Receiver
Master Receiver is used when a device would like to initialize a transfer and request data from a Slave
Transmitter. Master Receiver mode initially begins in the exact same way as the Master Transmitter.First the Master must take control of the bus by sending the START signal, then a Slave should be
addressed. The diꙸerence in slave addressing is that the R/W bit is 1. The TWI module will recognize
that the R/W bit is set for read mode and the module will enter into read mode.
The steps involved here;
1. Set the receive global receive buꙸer index (RXBuꙸIndex) to 0 and save the number of bytes to be
read to the global receive buꙸer length (RXBuꙸLen). The address of the slave to read from should
be placed in the transmit buꙸer.
2. Use the TWITransmitData function described above to transmit the slave address. Since the read
bit is now set, the TWI module will return status codes relating to read mode. Hence the
functionality will diverge from what is described above after the interrupt vector is 觡rst entered.
3. After the address byte and read bit have been transferred, there are three possible states;
Transmission success: A slave identi觡es its address and holds the data line low in the 9th
clock cycle (sends an ACK). Status code: 0x40
Transmission failed: No ACK is received in the 9th clock cycle. Either there is no slave with
that address, the slave is busy/intentionally not responding, TWI lines are damaged. I this
case it is up to the application software if the transmission should be retired or aborted. To
continue, send a STOP/START or Repeated START and try again. Status code: 0x48
Transmission failed: Arbitration has been lost – this will be discussed later and is beyond the
scope at this point. Status code: 0x38
4. Assuming the address byte is successfully transferred and an ACK was received, The application
enters Master Receiver mode and the transmission can continue. The device now expects to
receive data. After receiving a byte, there are two options;
After receiving a byte, hold the SDA line LOW on the ninth clock cycle in order to
567891011121314
151617
if (TWIInfo.repStart) {
TWIInfo.errorCode = TWI_STATUS; TWISendStart(); } // All transmissions are complete, exit else { TWIInfo.mode = Ready; TWIInfo.errorCode = TWI_STATUS;
TWISendStop(); } break;
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
16/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 16
acknowledge the byte and continue transfer. Status code: 0x50
After receiving a byte, transmit NACK by not holding SDA LOW. This indicates to the slave
that the transfer is complete and not to send any more bytes. Status Code: 0x58
5. For each byte received, retrieve it from the data register TWDR, add it to the receive buꙸer and
increment the buꙸer index. Once the byte is saved, continue the transfer and ACK the next byte
using the TWISendACK() macro.
6. Once there is only one byte left to retrieve, instead of continuing the transfer and acking the next
byte, instead continue but NACK the next byte using the TWISendNACK() macro.
7. All bytes have now been received, send STOP to end transfer and release bus or send a Repeated
START to end transfer and retain bus control.
The TWIReadData function is much simpler than the transmit function. I will not go into detail about it, I
think it speaks for itself:
So we now jump into the interrupt and handle everything from there. Again, there are de觡nes to
improve readability of status codes:
Step 3 (success), 4: After successfully addressing a slave, the TWINT ag will be set and the MCU jumps
to the interrupt. The status will be TWI_MR_SLAR_ACK. We need to set the TWIifo.mode to
MasterReceiver and then resume the transfer, instructing the TWI module to reply to the next data byte
1234567891011121314
15161718192021
uint8_t TWIReadData(uint8_t TWIaddr, uint8_t bytesToRead, uint8_t repStart){ // Check if number of bytes to read can fit in the RXbuffer if (bytesToRead < RXMAXBUFLEN) { // Reset buffer index and set RXBuffLen to the number of bytes to read RXBuffIndex = 0; RXBuffLen = bytesToRead; // Create the one value array for the address to be transmitted uint8_t TXdata[1]; // Shift the address and AND a 1 into the read write bit (set to write mode) TXdata[0] = (TWIaddr
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
17/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 17
with an ACK (TWISendACK()) if there is more than one byte to read, or a NACK (TWISendNACK()) if there is
only one byte.
Step 5: After successfully receiving a byte and replying with an ACK, the status will be
TWI_MR_DATA_ACK and the interrupt vector will be entered. Here, we save the byte to the receive
buꙸer, increment its index and then do the same as in step 3. If there is more than one more byte left
to read then we resume transfer and reply reply with ACK after receiving a byte, otherwise reply with
NACK.
Step 6: Step 5 is repeated until the last byte, in which case the TWI module will reply with a NACK to tell
the Slave to stop sending. After this byte has been received and the NACK has been sent, the MCU will
jump to the interrupt and the status register will read TWI_MR_DATA_NACK. Now we read the last byte
out of the data register TWDR and 觡nish the transfer by sending STOP or by sending a Repeated START.
12345
678910111213141516
case TWI_MR_SLAR_ACK: // SLA+R has been transmitted, ACK has been received // Switch to Master Receiver mode TWIInfo.mode = MasterReceiver; // If there is more than one byte to be read, receive data byte and retur
if (RXBuffIndex < RXBuffLen-1)
{ TWIInfo.errorCode = TWI_NO_RELEVANT_INFO; TWISendACK(); } // Otherwise when a data byte (the only data byte) is received, return NA else { TWIInfo.errorCode = TWI_NO_RELEVANT_INFO; TWISendNACK(); } break;
12345
67891011121314151617
case TWI_MR_DATA_ACK: // Data has been received, ACK has been transmitted.
/// -- HANDLE DATA BYTE --- /// TWIReceiveBuffer[RXBuffIndex++] = TWDR; // If there is more than one byte to be read, receive data byte and retur
if (RXBuffIndex < RXBuffLen-1) { TWIInfo.errorCode = TWI_NO_RELEVANT_INFO; TWISendACK(); } // Otherwise when a data byte (the only data byte) is received, return NA else { TWIInfo.errorCode = TWI_NO_RELEVANT_INFO; TWISendNACK(); } break;
12345
case TWI_MR_DATA_NACK: // Data byte has been received, NACK has been transmit
/// -- HANDLE DATA BYTE --- /// TWIReceiveBuffer[RXBuffIndex++] = TWDR;
// This transmission is complete however do not release bus yet
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
18/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 18
After beginning a read operation, the application should wait for the error code to be set to SUCCESS
(0xFF) before reading the data from the receive buꙸer.
Finally, non successful transfers need to be handled. There are two error codes to deal with;
TWI_MR_SLAR_NACK (0x48): Slave address + read bit transferred, NACK received
TWI_LOST_ARBIT (0×38): Arbitration lost in sending address bit or in ACK/NACK response
These error codes are handled in the same was as the Master Transmitter error codes (in fact, the
arbitration lost is the exact same error code). So just add these above the case for the previous code
without a break:
TWI MT/MR Testing
So we should now have fully working code for Master Transmitter and Master Receiver TWI application.
I test this using my FDCC LCD library, an ATMEGA1284P @ 8MHz and a 1Mbit EEPROM (24AA1025). A
string is written to the EEPROM, read back then displayed on the LCD.
6789101112131415
161718
if (TWIInfo.repStart) { TWIInfo.errorCode = 0xFF; TWISendStart(); } // All transmissions are complete, exit else { TWIInfo.mode = Ready; TWIInfo.errorCode = 0xFF;
TWISendStop(); } break;
123
45678910111213141516
1718
case TWI_MR_SLAR_NACK: // SLA+R transmitted, NACK received case TWI_MT_SLAW_NACK: // SLA+W transmitted, NACK received case TWI_MT_DATA_NACK: // Data byte has been transmitted, NACK received
case TWI_LOST_ARBIT: // Arbitration has been lost // Return error and send stop and set mode to ready if (TWIInfo.repStart) {
TWIInfo.errorCode = TWI_STATUS; TWISendStart(); } // All transmissions are complete, exit else { TWIInfo.mode = Ready; TWIInfo.errorCode = TWI_STATUS; TWISendStop();
} break;
http://www.chrisherring.net/all/fdcc-lcd-alphanumeric-display-library-for-avr/
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
19/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 19
12345678910111213141516171819202122
23242526272829303132333435
36373839404142434445464748
49505152535455565758596061
#include #include "FDCC_LCD.h"#include "TWIlib.h"#include #include #include #include int main(void){ // Enable Global interrupts sei(); // Initialize the ports for the FDCC LCD display FDCC_initPorts(); // Initialize the LCD FDCC_init(1, 1, 1, 0, 1, 1); // Initialize TWI TWIInit(); // Clear LCD screen and return cursor to home position FDCC_clearScreen(); FDCC_goHome(); // The EEPROM is set at address 1010 000
uint8_t TWIaddr = 0x50; // Set the write address uint8_t TWIaddrW = (TWIaddr
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
20/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/ 20
And as you can see, it WORKS!
Hello World! written to EEPROM then read back and
displayed on the LCD
The ATMEGA and the EEPROM on
heh Breadboard
62636465666768697071
7273747576777879808182
TWITransmitData(TXdata, 3, 1); }
// Great, now data is written, address is set. Now read the data back TWIInfo.errorCode = TWI_NO_RELEVANT_INFO; while (TWIInfo.errorCode != 0xFF) { TWIReadData(TWIaddr, dataLen, 0); }
// Wait until the TWI has finished before the data is available while (isTWIReady() == 0) {_delay_ms(1);} // TWI has finished, write the data to the LCD FDCC_sendString((char *)TWIReceiveBuffer); // I always flash a LED at the end in my test code.. just so you know everything
while(1) {
PORTD ^= 0x40; _delay_ms(500); }}
http://www.chrisherring.net/wp-content/uploads/2014/03/ATMEGAandEEPROM.jpghttp://www.chrisherring.net/wp-content/uploads/2014/03/HelloWorldTWI.jpg
-
8/19/2019 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1_ MT_MR) _ ChrisHerring
21/21
2/12/2016 [TUTORIAL] Interrupt Driven TWI Interface for AVR (Part 1: MT/MR) | ChrisHerring.net
Stay tuned for part two in which a Slave Transmitter and Slave Receiver mode will be implemented.
Chris
This entry was posted in All, Tutorials on March 8, 2014 [http://www.chrisherring.net/all/tutorial-
interrupt-driven-twi-interface-for-avr-part1/] .
http://www.chrisherring.net/all/tutorial-interrupt-driven-twi-interface-for-avr-part1/http://www.chrisherring.net/category/all/tutorials/http://www.chrisherring.net/category/all/