EEPROM

STM32 write and read EEPROM over I2C bus

EEPROM write and read

EEPROM sounds intimidating for the beginners, probably because there are few rules to comply. First, all EEPROMs share the same address on I2C bus, at least first page, and that is 0x50. I will give example for Atmel 24C08 chip, which has 8 kbit (!) memory. This number is NOT killo-bytes, but 1024 x 8 bits. So, practically ‘only’ 1 KB of memory space. Second rule is that writing must be done in sequence(s) of 8 or 16 bytes, depending of memory type. 1k and 2k EEPROMs can write only 8 bytes at a time, but 4k/8k/16k can write 16 bytes at a time. Between each write cycles and write then read cycle should be about 2 mS delay. This delay is some intrinsic property of the memory, and we can’t do anything about that. Only follow the rule. Read is possible whole ‘page’ of 256 bytes at once. Also, there is no restriction between two readings. Only after writing even singly byte, must be some delay, experimentally found 1.68 ms, so better use 2 mS (2000 uS) for sure.

Splitting data into groups of 16 bytes

That is how it should works. I made relatively simple code for STM32f10x family of the MCUs. In this code, there is two examples, one writing just 16 bytes, another one writing more than that in few steps with delay of 2 mS between each ‘packets’ of 16 bytes. Second example uses second of four pages. First example is on first page. Each page has actually its own I2C address ranging from 0x50 to 0x57 for 16k EEPROMs. I have only one chip that has 8k, so it covers four pages; page 0 = 0x50, page 1 = 0x51, page 2 = 0x52, and page 3 = 0x53. I found this chip below board with STM32f103VET6, that was surprise for me. Did not found any data about that board, nor it is mentioned in STM32 literature. And since this STM32 board has no ‘name’ as is for example Arduino uno, no data about this one except few words on eBay (plus price tag 😀 ).

In the example code I did not make algorithm for writing whole chip, because in practice this type of memory is just for few variables, maybe some calibration data or whatever user need to change after programming MCU, or during. For example, some servo has offset where middle position is not exactly in the middle. So, we can make code that scan buttons which moves servo, and when servo is where we want to be, another button press save calibration data into EEPROM. Since I did not use this chip in the past, I can’t give any example for now, but for sure it will be here in the future.

Code(s) not complete. Why?!

Please look carefully the examples. First example is not implemented correctly. I have doubt – do I need finish everything to show you, or you can learn something and recognize how to solve ‘the puzzle’? Second example, just un-comment (remove ‘//’) two separate functions twiSend(), twiReceive() and one printMsg() . That is last printMsg() which read all 255 bytes from second page at 0x51.  Also, you may notice that there are three strange variables included: ‘num’, ‘mantissa’ and ‘fraction’. Variable ‘num’ uses function strlen(test2) to get number of characters needed for two ‘for(;;)’ loops. In for example we have 92 characters, then 92/16 =5.75. Mantissa is number 5 (currently no needed in those examples), 0.75 is fraction, but (!) expressed in remaining bytes, that is 0.75*16=12.

Very interesting first loop:

for (int p=0;p<(num-fraction);p+=16)

This one uses number of characters (for example 92), subtract fraction (say 12), then it goes NOT from 0 to 92, but from 0 to 80 in steps of 16. Then some conversion of characters into uint8_t form. Not ideal, but… Then function twiSend(0x51,p,16) sends first 16 bytes, then another 16 until reaches 80. Then it exits for(;;) loop, and send the remaining 12 bytes twiSend(0x51,(num-fraction),fraction). At this time, ‘num-fraction’ is 92-12=80, which means that it begins to write at position 80 in EEPROM memory, for next ‘fraction’, which is 12 bytes.  After you copy/paste those codes, please align everything, because operation copy/paste onto this page can ruing alignment.

Here are the codes:

/*placeholder for page URL
This is just an example. You can modify this code (it is free)
as you wish and adapt to your needs. 
*/

#include "stm32f10x.h"
#include "printMsg.h"
#include "wire.h"
#include "delayUs.h"
#include "string.h"

//uint8_t address=0; 
uint8_t buffer[1024];

int main()
{
	usart_1_enable(); //enabling printMsg();
	twiEnable();//enabling two wire interface twiEnable(); that is our I2C1
	
/*Next function wroks only if you #include "printMsg.h" in library (also #include wire.h)	wire.c 
	(is included when you include .h lib.), there is already printMsg() function that will send to 
	USART1 message about addresses that is found on the I2C bus (here, only I2C1 bus). Note that 
	some STM32 boards has already EEPROM (8 Kbit) on the bottom of the PCB. Example is STM32F103VET6 */
	
 	//twiScan();//before anything else, lets check which devices are on the bus, 

	 /* EEPROM example writting and reading 16 bytes (4K/8K/16K only) at first page (0x50) with some message */

	char test[]={"This is for test"}; //test write to EEPROM, 1K/2K only 8 bytes, 4K/8K/16K maximum 16 bytes at once
	//            1234567890123456    (helper to see when it 'fit' into 16 bytes for 4K/8K/16K EEPROMs, can be less than 16 bytes, but not more)

	for (int i=0;i<strlen(test);i++)
	{
		buffer[i]=test[i]; //filling (uint8_t)buffer[] with (char)test
	}
  //twiSend(0x50,0,strlen(test)); //sending first 'packet' to the EEPROM at address 0x50 from position 0
  delay(2000); //minimum time to wait is 1.658 mS, so use 1 mS, or better 2000 uS, else it will stuck
  //twiReceive(0x50,0,40); //receiver from first page at 0x50
  for (int i=0;i<strlen(test);i++)
	{
		//printMsg("%c",buffer[i]);//here %c means that we will print characters. If you want, you may try %c and you will get ASCII values
	}
	
	/* EEPROM example writing and reading more than 16 bytes in few sequences*/
	
	char test2[]={"This is example of writing EEPROM memory more than 16 bytes - in sequences of 16 bytes + 12 ."};
//             12345678901234567890123456789012345678901234567890112345678901234567890123456789012345678901234567890
	//             |       10     |  20        30 |      40       |50         60   |    70        8|0       90     |   100... (decades)
	//             0             16              32              48               64              80              96...  (hexadecimals)
 
	//int mantissa; 
	int fraction; 
	int num;
	
	num=strlen(test2);
	//mantissa=num/16;
	fraction=num%16;
	
	for (int p=0;p<(num-fraction);p+=16)
	{
	  for (int b=0;b<16;b++)
		{
			buffer[b]=test2[b+p];//lets put packets of 16 bytes into buffer
		}
		twiSend(0x51,p,16);	//second page (0x51), but you can do it at any other pages from 0x50 to 0x57 (if your EEPROM has that much memory)
    delay(2000); //wait 2 mS between two EEPROM access
	}
	for (int i=0;i<fraction;i++)
	{
		buffer[i]=test2[(num-fraction)+i];
	}
	delay(2000); //wait 2 mS between two EEPROM access
  twiSend(0x51,(num-fraction),fraction); 

	delay(2000);
	//twiReceive(0x50,0,120); //receiving bytes stored in EEPROM in the first example written way above, first page
	//for (int i=0;i<255;i++) printMsg("%c",buffer[i]); printMsg("\n");
	
	delay(2000);
	twiReceive(0x51,0,255); //receiving bytes stored in EEPROM in the second example above, second page
	for (int i=0;i<255;i++)	printMsg("%c",buffer[i]); printMsg("\n");
}
/* Two Wire Interface, I2C (or IIC), here will be called 'twi', and we have
   only twiEnable(), twiSend() and twiReceive(). The twiSend() function is 
	 fairly simple, we just send address of the device shifted to the left by
	 1 bit, or-red | zero (0) at free space that tell I2C bus it is for write operation.
	 The receive twiReceive() function works by sending address also shifted left
	 one bit with logic or | zero (0) at empty bit (LSB), but then we must send command 
	 to the device depending what device has. After command, we stop (although
	 we can remove STOP condition and continue to "repeated start", then we
	 must change bit after address of the device, now it is one (1) that tells
	 I2C bus we want to read. If we try only read from some address, device
	 don't know what to send. So we must first issue command, then read. For
	 specific command set read datasheet of particular device - it is different
	 for all different devices. More on my website: http://wp.me/p7jxwp-nD */

#include "stm32f10x.h"
#include "delayUs.h"
#include "wire.h"
#include "printMsg.h"

void twiEnable(void) 
{
  //just set all registries, but NOT START condition - execute once in main.c
	RCC->APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN; //B port enabled, alternate function 
	RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; //I2C 1 enabled 
	GPIOB->CRL = 0xFF000000;// setting just pins B7 (SDA) and B6 (SCL), while leaving the rest intact 50 MHz!
	I2C1->CR2 |= 50; // GPIO clock freq=50 MHz MUST !!! be equal APB frequency (GPIO, 2, 10 or 50 MHz)
	I2C1->CCR |= I2C_CCR_FS; //fast mode
	I2C1->CCR |= 30; //not sure for 400 000 - (10= 1.2 MHz, 15=800 kHz, 30=400 kHz)
	I2C1->TRISE |= 51; // maximum rise time is 1000 nS
	I2C1->CR1 |= I2C_CR1_PE; 
}

void twiScan(void)
{		int a=0; 
	 	for (uint8_t i=0;i<128;i++)
   {
			I2C1->CR1 |= I2C_CR1_START;
			while(!(I2C1->SR1 & I2C_SR1_SB));
			I2C1->DR=(i<<1|0); 
			while(!(I2C1->SR1)|!(I2C1->SR2)){}; 
			I2C1->CR1 |= I2C_CR1_STOP; 
			delay(100);//minimum wait time is 40 uS, but for sure, leave it 100 uS
			a=(I2C1->SR1&I2C_SR1_ADDR);
			if (a==2)
		 {
				printMsg("Found I2C device at adress 0x%X (hexadecimal), or %d (decimal)\n",i,i);
		 }
	 }
}
	
/* Command or commands, or sending bytes, just the same name of the variable 'command' */
void twiSend(uint8_t address, uint8_t command, uint8_t length)
{
	I2C1->CR1 |= I2C_CR1_START; //START condition 
	while(!(I2C1->SR1 & I2C_SR1_SB));
	I2C1->DR=(address<<1|0); //sending address of the device, 0 = sending
  while(!(I2C1->SR1 & I2C_SR1_ADDR)|!(I2C1->SR2));		
	I2C1->DR=command; //filling data register with byte, if single - command, multiple - command(s) and data
	for (uint8_t i=0;i<length;i++)
	{ 
		I2C1->DR=buffer[i]; //filling buffer with command or data
		delay(60);
	}
	I2C1->CR1 |= I2C_CR1_STOP;
}

void twiReceive(uint8_t address, uint8_t command, uint8_t length) 
{
	I2C1->CR1 |= I2C_CR1_ACK;
  I2C1->CR1 |= I2C_CR1_START; //start pulse 
	while(!(I2C1->SR1 & I2C_SR1_SB));
	I2C1->DR=(address<<1|0); //sending address of the device, 0 = sending
	while(!(I2C1->SR1 & I2C_SR1_ADDR)|!(I2C1->SR2 & I2C_SR2_BUSY));
	I2C1->DR=command; //sending command to the device in order to request data
	I2C1->CR1 |= I2C_CR1_START; //REPEATED START condition to change from sending address + command to receive data
	while(!(I2C1->SR1 & I2C_SR1_SB));
	I2C1->DR=(address<<1|1); //sending address of the device, 1 = reading 
	while(!(I2C1->SR1 & I2C_SR1_ADDR)|!(I2C1->SR2));
	
if (length==1)  //receiving single byte, N=1
	{
		while(!(I2C1->SR1)|!(I2C1->SR2));
		I2C1->CR1 &= ~I2C_CR1_ACK; //this will send later NAK (not acknowledged) to signal it is last byte
		I2C1->CR1 |= I2C_CR1_STOP; //issuing STOP condition before (!) reading byte
		buffer[0]=I2C1->DR; //single byte is read AFTER NAK (!) and STOP condition
	} 
	if (length==2) //receiving two bytes, N=2
	{
		while(!(I2C1->SR1)|!(I2C1->SR2));
		I2C1->CR1 &= ~I2C_CR1_ACK; //this will send later NAK (not acknowledged) before last byte
    I2C1->CR1 |= I2C_CR1_STOP;
		buffer[0]=I2C1->DR; //reading N-1 byte, next to last byte is in DR, last one still in shift register
		while(!(I2C1->SR1 & I2C_SR1_RXNE)|!(I2C1->SR2));
		buffer[1]=I2C1->DR; //read last N byte now available 
	} 
  if (length>2) //receiving more than two bytes, N>2
	{
		
	  for (uint8_t i=0;i<length;i++)
	  { 
			                     
		  if (i<(length-3))      // if it is not N-2, then read all bytes
			{
				while(!(I2C1->SR1 & I2C_SR1_RXNE)|!(I2C1->SR2));
				buffer[i]=I2C1->DR;  
			}
		  else if (i==length-3) // if it is N-2 then read 
			{
				while(!(I2C1->SR1)|!(I2C1->SR2));
				buffer[i]=I2C1->DR; 
				while(!(I2C1->SR1 & I2C_SR1_RXNE)|!(I2C1->SR2));
				I2C1->CR1 &= ~I2C_CR1_ACK; //this will send later NAK (not acknowledged) before last byte
				I2C1->CR1 |= I2C_CR1_STOP;
			}
	    else if (i==length-2) // if it is N-1 then read
			{
				while(!(I2C1->SR1 & I2C_SR1_RXNE)|!(I2C1->SR2));
				buffer[i]=I2C1->DR; 
			}
			else if (i==length-1) // else it is N byte 
			{
				while(!(I2C1->SR1 & I2C_SR1_RXNE)|!(I2C1->SR2)){};
		    buffer[i]=I2C1->DR;  
			}
    } 
 }
}
#ifndef wire_h
#define wire_h

#include <stdint.h>

extern uint8_t  address, command, length; 
extern uint8_t  buffer[];
extern uint8_t  status1;
extern uint8_t  status2;

void twiEnable(void);
void twiScan(void);
void twiSend(uint8_t address, uint8_t command, uint8_t length);
void twiReceive(uint8_t address, uint8_t command, uint8_t length);


#endif
#include "stm32f10x.h"
#include "stdint.h"
#include <stdio.h>
#include "stdarg.h"
#include "string.h"
#include "printMsg.h"

char buff[256];

void usart_1_enable(void)
{
  //enabling pin A9 for alternating funct. for uart/usart
  RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN | RCC_APB2ENR_USART1EN; //clock to GPIO A enabled, port A(2), alt.funct.en(0), usart1 clock enabled(14)
	GPIOA->CRH |= GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9_0 | GPIO_CRH_MODE9_1; //port A9
	GPIOA->CRH &= ~GPIO_CRH_CNF9_0; //port A9
	GPIOA->CRH &= ~(GPIO_CRH_MODE10_0|GPIO_CRH_MODE10_1); //port A10 is RX
	GPIOA->CRH |= GPIO_CRH_CNF10_0; //port A10 is RX
	
	//GPIOA->CRH = 0x444444B4; // A9 is alternate output, 50 MHz, push-pull - not this time short version (!)
	//clkPer/(baudRx_16bit)=72MHZ/9600 = 7500 = 0x1D4C
	/* Remove comment line for speed that you want to use*/
	//USART1->BRR = (0xEA60); //   1200 Baud
	//USART1->BRR = (0x7530); //   2400 Baud
	//USART1->BRR = (0x3A98); //   4800 Baud
	//USART1->BRR = (0x1D4C); //   9600 Baud
	//USART1->BRR = (0x1388); //  14400 Baud
	//USART1->BRR = (0xEA6) ; //  19200 Baud
	//USART1->BRR = (0x9c4) ; //  28800 Baud
	//USART1->BRR = (0x753) ; //  38400 Baud
	//USART1->BRR = (0x505) ; //  56000 Baud
	//USART1->BRR = (0x4E2) ; //  57600 Baud
	USART1->BRR = (0x271) ; // 115200 Baud
	//USART1->BRR = (0x232) ; // 128000 Baud
	//USART1->BRR = (0x119) ; // 256000 Baud
	//USART1->BRR = (0x8C)  ; // 512000 Baud
	//USART1->BRR = (0x46)  ; // 1024000 Baud
	//USART1->BRR = (0x23)  ; // 2048000 Baud
  //USART1->BRR = (0x18)  ; // 3000000 Baud (3 MHz, max speed that HTerm can get, non-standard speed)
	
	
	USART1->CR1 |= USART_CR1_TE; //transmitter enable
	USART1->CR1 |= USART_CR1_RE; //receiver enable
	USART1->CR1 |= USART_CR1_UE; //usart enable
}

void printMsg(char *msg, ...)
{
	//char buff[120]; //was 80
	va_list args;
	va_start(args,msg); 
	vsprintf(buff,msg,args);

	for(int i=0;i<strlen(buff);i++)
	{
	  USART1->DR = buff[i];
	  while(!(USART1->SR & USART_SR_TXE)); //wait for TXE, 1 = data transferred
  }
}
#ifndef printMsg_h
#define printMsg_h

extern int len;
extern char buff[];
//char buff[];

void usart_1_enable(void);
void printMsg(char *msg, ...);

#endif
void delay(unsigned long cycles)
{
  while(cycles >0)
	{
		asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
		asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
		asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
		asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
		asm("nop");asm("nop");asm("nop");  //to get 1 uS if delay(1)
  cycles--; 
	}
}
#ifndef delayUs_h
#define delayUs_h

extern void delay(unsigned long cycles);

#endif

Copy/paste all codes and save in the same directory for Keil. I am not sure but I think the same codes can work in other editors/compilers/assemblers, but I am not familiar with those.

4 thoughts on “STM32 write and read EEPROM over I2C bus”

  1. Hi Milan,

    Great tutorials, thank you. This reading code works fine for me.

    void i2cRead(uint8_t devAddr, uint8_t dataAddr, uint8_t size)
    {
    //I2C1->OAR1 |= ( 0x68 <CR1 |= I2C_CR1_START | I2C_CR1_ACK;
    while(!(I2C1->SR1 & I2C_SR1_SB));

    // say “hello, i want to write” then control if address is matched
    I2C1->DR = (devAddr <SR1 & I2C_SR1_ADDR)| !(I2C1->SR2 & I2C_SR2_BUSY));

    // sending data address that i want to read
    I2C1->DR = dataAddr;
    while(!(I2C1->SR1 & I2C_SR1_TXE));

    // restart required for changing mode to reading mode
    I2C1->CR1 |= I2C_CR1_START;
    while(!(I2C1->SR1 & I2C_SR1_SB));

    // say “hello, i want to read data from data address that i just send” then control if address is matched
    I2C1->DR = (devAddr <SR1 & I2C_SR1_ADDR)| !(I2C1->SR2 & I2C_SR2_BUSY));

    // this part is needed for reading multiple bytes
    if(size > 1)
    {
    for(uint8_t i = 0; i SR1 & I2C_SR1_RXNE));
    received[i] = I2C1->DR;
    }
    }

    // stop communication and read last byte
    I2C1->CR1 &= ~I2C_CR1_ACK;
    I2C1->CR1 |= I2C_CR1_STOP;
    while(I2C1->CR1 & I2C_CR1_STOP);
    received[size – 1] = I2C1->DR;
    delay(1000);
    }

  2. Hi, Mustafa K

    I am glad you found better solution. Just there is something weird with part of the code, can you repeat? For example, here:
    for(uint8_t i = 0; i SR1 & I2C_SR1_RXNE));
    received[i] = I2C1->DR;
    Seems that something is mixed during copy/paste. Okay, I will contact you over the e-mail. Thanks.

  3. Sorry for late response Milan. Here is main.

    Mustafa

    /**
    ******************************************************************************
    * @file main.c
    * @date 30-March-2019
    * @brief Read MPU6050 sensor data through I2C
    * @brief B6 – MPU6050 SCL
    * @brief B7 – MPU6050 SDA
    ******************************************************************************
    */

    #include “stm32f10x.h”

    #define MPU6050Address 0x68
    #define PWR_MGMT_1 0x6B
    #define WHO_AM_I 0x75
    #define MPU6050_WAKEUP 0x00
    #define MPU6050_SLEEP 0x40
    #define TEMP_OUT_H 0x41
    #define TEMP_OUT_L 0x42

    void delay(uint32_t delay);
    void i2cInitialization(void);
    void i2cRead(uint8_t devAddr, uint8_t regAddr, uint8_t size);
    void i2cWrite(uint8_t devAddr, uint8_t regAddr, uint8_t data);
    int16_t i2cMPU6050Temp(void);

    uint8_t received[8];
    int16_t temp = 0x0000;

    #if 1
    int main(void)
    {
    i2cInitialization();

    i2cWrite(MPU6050Address, PWR_MGMT_1, MPU6050_WAKEUP);

    while(1)
    {
    int16_t temp = i2cMPU6050Temp();
    delay(1000000);
    }
    }
    #endif

    #if 0
    int main(void)
    {
    // Enable clock for port B

    RCC->APB1ENR &= ~(RCC_APB1ENR_I2C2EN);
    I2C2->CR1 = 0x00;
    I2C2->CR2 =0x00;

    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
    RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;

    I2C2->CR2 |= 36; // 36 MHz APB1ENR
    I2C2->CCR = 45; // 400kHz I2C
    I2C2->CCR |= I2C_CCR_FS;
    I2C2->TRISE |= 37;
    I2C2->CR1 |= I2C_CR1_ACK;

    // GPIO Settings | Alternate Function | Open Drain | 50MHz
    GPIOB->CRH |= GPIO_CRH_CNF10 | GPIO_CRH_MODE10;
    GPIOB->CRH |= GPIO_CRH_CNF11 | GPIO_CRH_MODE11;

    I2C2->OAR1 |= ( 0x68 <CR1 |= I2C_CR1_START;
    while(I2C2->SR1 & I2C_SR1_SB);

    I2C2->DR = 0xd0;
    while(I2C2->SR1 & I2C_SR1_ADDR);

    I2C2->DR = 0x75;
    while(!(I2C2->SR1 & I2C_SR1_TXE));

    I2C2->CR1 |= I2C_CR1_START;
    while(I2C2->SR1 & I2C_SR1_SB);

    I2C2->DR = 0xd1;
    while(!(I2C2->SR1 & I2C_SR1_TXE));

    while(!(I2C2->SR1 & I2C_SR1_RXNE));
    char received = I2C2->DR;

    I2C2->CR1 |= I2C_CR1_STOP;
    while(I2C2->CR1 & I2C_CR1_STOP);

    while(1)
    {

    }
    }
    #endif

    void delay(uint32_t delay)
    {
    int i = 0;
    while(i OAR1 |= ( 0x68 <CR1 |= I2C_CR1_START | I2C_CR1_ACK;
    while(!(I2C1->SR1 & I2C_SR1_SB));

    // say “hello, i want to write” then control if address is matched
    I2C1->DR = (devAddr <SR1 & I2C_SR1_ADDR)| !(I2C1->SR2 & I2C_SR2_BUSY));

    // sending to data address that i want to read
    I2C1->DR = regAddr;
    while(!(I2C1->SR1 & I2C_SR1_TXE));

    // restart required for changing mode to reading mode
    I2C1->CR1 |= I2C_CR1_START;
    while(!(I2C1->SR1 & I2C_SR1_SB));

    // say “hello, i want to read data from data address that i just send” then control if address is matched
    I2C1->DR = (devAddr <SR1 & I2C_SR1_ADDR) | !(I2C1->SR2 & I2C_SR2_BUSY));

    // this part is needed for reading multiple bytes
    if(size > 1)
    {
    for(uint8_t i = 0; i SR1 & I2C_SR1_RXNE));
    received[i] = I2C1->DR;
    }
    }

    // stop communication and read last byte
    I2C1->CR1 &= ~I2C_CR1_ACK;
    I2C1->CR1 |= I2C_CR1_STOP;
    while(I2C1->CR1 & I2C_CR1_STOP);
    received[size – 1] = I2C1->DR;
    delay(1000);
    }

    void i2cInitialization(void)
    {
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
    // GPIO Settings | Alternate Function | Open Drain | 50MHz
    GPIOB->CRL |= GPIO_CRL_CNF6 | GPIO_CRL_MODE6;
    GPIOB->CRL |= GPIO_CRL_CNF7 | GPIO_CRL_MODE7;

    delay(100000);

    // I2C Initialization
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
    I2C1->CR2 |= 50; // 50MHz
    //I2C1->CCR |= I2C_CCR_FS;
    I2C1->CCR |= 180;
    I2C1->TRISE |= 51;
    I2C1->CR1 |= I2C_CR1_PE;

    delay(1000);
    }

    void i2cWrite(uint8_t devAddr, uint8_t regAddr, uint8_t data)
    {
    // Set ACK and Start then control start bit
    I2C1->CR1 |= I2C_CR1_START | I2C_CR1_ACK;
    while(!(I2C1->SR1 & I2C_SR1_SB));

    // say “hello, i want to write” then control if address is matched
    I2C1->DR = (devAddr <SR1 & I2C_SR1_ADDR)| !(I2C1->SR2 & I2C_SR2_BUSY));

    // sending to data address that i want to read
    I2C1->DR = regAddr;
    while(!(I2C1->SR1 & I2C_SR1_TXE));

    // sending to data that i want to read
    I2C1->DR = data;
    while(!(I2C1->SR1 & I2C_SR1_TXE));

    // stop communication and read last byte
    I2C1->CR1 &= ~I2C_CR1_ACK;
    I2C1->CR1 |= I2C_CR1_STOP;
    while(I2C1->CR1 & I2C_CR1_STOP);
    delay(1000);
    }

    int16_t i2cMPU6050Temp(void)
    {
    i2cRead(MPU6050Address, TEMP_OUT_H, 2);
    temp |= received[0] << 8 | received[1];
    temp = (float)(temp / 340) + 36.53;
    return temp;
    }

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.