In the previous guide (here), we saw how to read three different register from a slave device (DS3231 RTC) and display the time stored in the RTC. In this guide, we shall see how to write multiple bytes.
In this guide, we shall cover the following:
- I2C write multiple bytes.
- I2C write multiple bytes code.
- Code.
- Demo.
1. I2C write multiple bytes:
To write multiple byte for a slave device, we start off by sending start condition then wait to start to be generated then send the slave address and wait for the address to set then sending the memory location (0x00 in our case) then the reset of the data as shown in figure below
With each byte transfer, we need to wait for Byte Transfer Finished (BTF in SR1 register).
2. I2C write multiple bytes code:
In i2c.c source file, we shall declare the following new function:
void i2c1_writeMemoryMulti(uint8_t saddr,uint8_t maddr, uint8_t *data, uint8_t length)
The function takes four arguments:
- Slave address.
- Memory address.
- Pointer to a buffer which hold the data to be written.
- Length of the buffer.
Within the function, we start off by waiting until the bus is free:
while (I2C1->SR2 & I2C_SR2_BUSY); //wait until bus not busy
After the bus is freed, send the start condition:
I2C1->CR1 |= I2C_CR1_START; //generate start
Wait until the start is generated:
while (!(I2C1->SR1 & I2C_SR1_SB)){;} //wait until start is generated
After the start is generated, send the slave address shifted to left by 1 bit.
The reason behind the shifting is the address takes the bit1 to bit7 from the data register and bit0 is used for read/write operation. When bit0 is 0, it the operation is write operation and when it is 1, it means read operation.
I2C1->DR = saddr<< 1; // Send slave address
Wait until the address is set:
while (!(I2C1->SR1 & I2C_SR1_ADDR)){;} //wait until address flag is set
Clear SR2 register:
(void) I2C1->SR2; //Clear SR2
Wait until transmit buffer is empty by checking TXE bit in SR1 register:
while (!(I2C1->SR1 & I2C_SR1_TXE)); //Wait until Data register empty
Send the memory address:
I2C1->DR = maddr; // send memory address
Wait until transmit buffer is empty by checking TXE bit in SR1 register:
while (!(I2C1->SR1 & I2C_SR1_TXE)); //wait until data register empty
Now, using for loop, we shall send the data one after the other:
Within the loop, waiting until byte is transmitted by checking BTF bit in SR1 register:
for (uint8_t i=0;i<length;i++) { I2C1->DR=data[i]; //filling buffer with command or data while (!(I2C1->SR1 & I2C_SR1_BTF)); }
Once all the data have been transmitted, send the stop condition:
I2C1->CR1 |= I2C_CR1_STOP;
Hence, the function as following:
void i2c1_writeMemoryMulti(uint8_t saddr,uint8_t maddr, uint8_t *data, uint8_t length) { while (I2C1->SR2 & I2C_SR2_BUSY); //wait until bus not busy I2C1->CR1 |= I2C_CR1_START; //generate start while (!(I2C1->SR1 & I2C_SR1_SB)){;} //wait until start is generated I2C1->DR = saddr<< 1; // Send slave address while (!(I2C1->SR1 & I2C_SR1_ADDR)){;} //wait until address flag is set (void) I2C1->SR2; //Clear SR2 while (!(I2C1->SR1 & I2C_SR1_TXE)); //Wait until Data register empty I2C1->DR = maddr; // send memory address while (!(I2C1->SR1 & I2C_SR1_TXE)); //wait until data register empty for (uint8_t i=0;i<length;i++) { I2C1->DR=data[i]; //filling buffer with command or data while (!(I2C1->SR1 & I2C_SR1_BTF)); } I2C1->CR1 |= I2C_CR1_STOP; }
Also, update the header file by adding the following function:
void i2c1_writeMemoryMulti(uint8_t saddr,uint8_t maddr, uint8_t *data, uint8_t length);
3. Code:
You may download the source code from here:
4. Demo:
Happy coding 🙂
Add Comment