In the pervious guid (here), we took a look how to write/read single byte using I2C and the slave device is DS3231
In this guide, we shall see how to read multiple byte of DS3231 which they are seconds, minutes and hours and display them on the serial monitor.
In this guide, we will cover the following:
- I2C read multiple byte
- I2C read multiple byte code
- Code Download
- Demo
1. I2C Read Multiple Byte:
In order to read multiple byte, first we send the start condition, then followed by the slave address with write bit then the memory location in our case ( seconds memory location 0x00). After that a repeat start must be generated and send slave address with read bit after that we enable acknowledge(ACK). We keep reading (in out case 3) until one byte remains then we disable acknowledge (NAK). The figure below shows the reading sequence.
2. I2C Read Multiple Byte Code:
We start of by declaring the function name as following:
void i2c_ReadMulti(char saddr,char maddr, int n, char* data){
it takes three arguments
- slave address
- starting address to read from
- number of byte to be read
- buffer to store the data
Inside the function we declare temp to clear flags
volatile int temp;
we wait until the bus is free
while (I2C1->SR2 & I2C_SR2_BUSY){;}
Send start condition and wait until start condition is generated
I2C1->CR1|=I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)){;}
Send the slave address with write operation and wait until address is set
I2C1->DR=saddr<<1; while(!(I2C1->SR1 & I2C_SR1_ADDR)){;}
Then clear the status register
temp=I2C1->SR2;
Then wait until transmit bit is set
while(!(I2C1->SR1&I2C_SR1_TXE)){;}
Now we send memory location
I2C1->DR = maddr;
Then we wait until transmit bit is set
while(!(I2C1->SR1&I2C_SR1_TXE)){;}
After that we regenerate start condition and wait until the start condition is generated
I2C1->CR1|=I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)){;}
Now, we send slave address with read bit and wait until the address is set
I2C1->DR=saddr<<1|1; while(!(I2C1->SR1 & I2C_SR1_ADDR)){;}
we clear the status register
temp=I2C1->SR2;
Then we enable acknowledge
I2C1->CR1|=I2C_CR1_ACK;
After that we start reading
we start of with while loop
while(n>0U) {
Now we have a little bit of machine state
In case we have only one byte left, we must do the following:
- Disable acknowledge
- Generate stop
- Wait until receive bit is set
- Store the last received byte to the buffer
- Break the while loop
In case more than one byte left
- Wait for receive bit to be set
- store the data in the buffer
- increment the buffer counter
- decrement the counter
if(n==1U) { I2C1->CR1&=~I2C_CR1_ACK; I2C1->CR1|=I2C_CR1_STOP; while(!(I2C1->SR1&I2C_SR1_RXNE)){;} *data++=I2C1->DR; break; } else { while(!(I2C1->SR1&I2C_SR1_RXNE)){;} (*data++)=I2C1->DR; n--; } }//for the while loop
Hence the code shall be like this:
void i2c_ReadMulti(char saddr,char maddr, int n, char* data) { volatile int temp; while (I2C1->SR2 & I2C_SR2_BUSY){;} I2C1->CR1|=I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)){;} I2C1->DR=saddr<<1; while(!(I2C1->SR1 & I2C_SR1_ADDR)){;} temp=I2C1->SR2; while(!(I2C1->SR1&I2C_SR1_TXE)){;} I2C1->DR = maddr; while(!(I2C1->SR1&I2C_SR1_TXE)){;} I2C1->CR1|=I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)){;} I2C1->DR=saddr<<1|1; while(!(I2C1->SR1 & I2C_SR1_ADDR)){;} temp=I2C1->SR2; I2C1->CR1|=I2C_CR1_ACK; while(n>0U) { if(n==1U) { I2C1->CR1&=~I2C_CR1_ACK; I2C1->CR1|=I2C_CR1_STOP; while(!(I2C1->SR1&I2C_SR1_RXNE)){;} *data++=I2C1->DR; break; } else { while(!(I2C1->SR1&I2C_SR1_RXNE)){;} (*data++)=I2C1->DR; n--; } } }
3. Code Download
You can download the code from here
4. Demo
After you compile and upload the code, open serial monitor program and set the baudrate at 115200 and you should see the serial is printing the time
Happy coding 🙂
Add Comment