{"id":1776,"date":"2019-03-02T00:38:11","date_gmt":"2019-03-01T23:38:11","guid":{"rendered":"http:\/\/wildlab.org\/?p=1776"},"modified":"2019-05-24T22:11:26","modified_gmt":"2019-05-24T21:11:26","slug":"stm32-programming-eeprom-over-i2c-bus","status":"publish","type":"post","link":"https:\/\/wildlab.org\/index.php\/2019\/03\/02\/stm32-programming-eeprom-over-i2c-bus\/","title":{"rendered":"STM32 write and read EEPROM over I2C bus"},"content":{"rendered":"<h2>EEPROM write and read<\/h2>\n<p>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 &#8216;only&#8217; 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&#8217;t do anything about that. Only follow the rule. Read is possible whole &#8216;page&#8217; 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.<\/p>\n<h2>Splitting data into groups of 16 bytes<\/h2>\n<p>That is how it should works. I made relatively simple code for <a href=\"https:\/\/www.banggood.com\/STM32F103C8T6-ARM-STM32-Mini-System-Development-Board-STM32F103-Core-Board-p-1207605.html?p=EH10221611330201505Q\" target=\"_blank\" rel=\"noopener noreferrer\">STM32f10x<\/a> 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 &#8216;packets&#8217; 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 &#8216;name&#8217; as is for example Arduino uno, no data about this one except few words on eBay (plus price tag \ud83d\ude00 ).<\/p>\n<p>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&#8217;t give any example for now, but for sure it will be here in the future.<\/p>\n<h2>Code(s) not complete. Why?!<\/h2>\n<p>Please look carefully the examples. First example is not implemented correctly. I have doubt &#8211; do I need finish everything to show you, or you can learn something and recognize how to solve &#8216;the puzzle&#8217;? Second example, just un-comment (remove &#8216;\/\/&#8217;) two separate functions twiSend(), twiReceive() and one printMsg() . That is last printMsg() which read all 255 bytes from second page at 0x51.\u00a0 Also, you may notice that there are three strange variables included: &#8216;num&#8217;, &#8216;mantissa&#8217; and &#8216;fraction&#8217;. Variable &#8216;num&#8217; uses function strlen(test2) to get number of characters needed for two &#8216;for(;;)&#8217; 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.<\/p>\n<p>Very interesting first loop:<\/p>\n<pre class=\"lang:c++ decode:true \">for (int p=0;p&lt;(num-fraction);p+=16)<\/pre>\n<p>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&#8230; 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, &#8216;num-fraction&#8217; is 92-12=80, which means that it begins to write at position 80 in EEPROM memory, for next &#8216;fraction&#8217;, which is 12 bytes.\u00a0 After you copy\/paste those codes, please align everything, because operation copy\/paste onto this page can ruing alignment.<\/p>\n<p><iframe loading=\"lazy\" title=\"STM32 writing and reading EEPROM programming in Keil\" width=\"474\" height=\"267\" src=\"https:\/\/www.youtube.com\/embed\/0xq_v1pyl-E?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<p>Here are the codes:<\/p>\n<pre class=\"lang:c++ decode:true\" title=\"EEPROM.c\">\/*placeholder for page URL\r\nThis is just an example. You can modify this code (it is free)\r\nas you wish and adapt to your needs. \r\n*\/\r\n\r\n#include \"stm32f10x.h\"\r\n#include \"printMsg.h\"\r\n#include \"wire.h\"\r\n#include \"delayUs.h\"\r\n#include \"string.h\"\r\n\r\n\/\/uint8_t address=0; \r\nuint8_t buffer[1024];\r\n\r\nint main()\r\n{\r\n\tusart_1_enable(); \/\/enabling printMsg();\r\n\ttwiEnable();\/\/enabling two wire interface twiEnable(); that is our I2C1\r\n\t\r\n\/*Next function wroks only if you #include \"printMsg.h\" in library (also #include wire.h)\twire.c \r\n\t(is included when you include .h lib.), there is already printMsg() function that will send to \r\n\tUSART1 message about addresses that is found on the I2C bus (here, only I2C1 bus). Note that \r\n\tsome STM32 boards has already EEPROM (8 Kbit) on the bottom of the PCB. Example is STM32F103VET6 *\/\r\n\t\r\n \t\/\/twiScan();\/\/before anything else, lets check which devices are on the bus, \r\n\r\n\t \/* EEPROM example writting and reading 16 bytes (4K\/8K\/16K only) at first page (0x50) with some message *\/\r\n\r\n\tchar test[]={\"This is for test\"}; \/\/test write to EEPROM, 1K\/2K only 8 bytes, 4K\/8K\/16K maximum 16 bytes at once\r\n\t\/\/            1234567890123456    (helper to see when it 'fit' into 16 bytes for 4K\/8K\/16K EEPROMs, can be less than 16 bytes, but not more)\r\n\r\n\tfor (int i=0;i&lt;strlen(test);i++)\r\n\t{\r\n\t\tbuffer[i]=test[i]; \/\/filling (uint8_t)buffer[] with (char)test\r\n\t}\r\n  \/\/twiSend(0x50,0,strlen(test)); \/\/sending first 'packet' to the EEPROM at address 0x50 from position 0\r\n  delay(2000); \/\/minimum time to wait is 1.658 mS, so use 1 mS, or better 2000 uS, else it will stuck\r\n  \/\/twiReceive(0x50,0,40); \/\/receiver from first page at 0x50\r\n  for (int i=0;i&lt;strlen(test);i++)\r\n\t{\r\n\t\t\/\/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\r\n\t}\r\n\t\r\n\t\/* EEPROM example writing and reading more than 16 bytes in few sequences*\/\r\n\t\r\n\tchar test2[]={\"This is example of writing EEPROM memory more than 16 bytes - in sequences of 16 bytes + 12 .\"};\r\n\/\/             12345678901234567890123456789012345678901234567890112345678901234567890123456789012345678901234567890\r\n\t\/\/             |       10     |  20        30 |      40       |50         60   |    70        8|0       90     |   100... (decades)\r\n\t\/\/             0             16              32              48               64              80              96...  (hexadecimals)\r\n \r\n\t\/\/int mantissa; \r\n\tint fraction; \r\n\tint num;\r\n\t\r\n\tnum=strlen(test2);\r\n\t\/\/mantissa=num\/16;\r\n\tfraction=num%16;\r\n\t\r\n\tfor (int p=0;p&lt;(num-fraction);p+=16)\r\n\t{\r\n\t  for (int b=0;b&lt;16;b++)\r\n\t\t{\r\n\t\t\tbuffer[b]=test2[b+p];\/\/lets put packets of 16 bytes into buffer\r\n\t\t}\r\n\t\ttwiSend(0x51,p,16);\t\/\/second page (0x51), but you can do it at any other pages from 0x50 to 0x57 (if your EEPROM has that much memory)\r\n    delay(2000); \/\/wait 2 mS between two EEPROM access\r\n\t}\r\n\tfor (int i=0;i&lt;fraction;i++)\r\n\t{\r\n\t\tbuffer[i]=test2[(num-fraction)+i];\r\n\t}\r\n\tdelay(2000); \/\/wait 2 mS between two EEPROM access\r\n  twiSend(0x51,(num-fraction),fraction); \r\n\r\n\tdelay(2000);\r\n\t\/\/twiReceive(0x50,0,120); \/\/receiving bytes stored in EEPROM in the first example written way above, first page\r\n\t\/\/for (int i=0;i&lt;255;i++) printMsg(\"%c\",buffer[i]); printMsg(\"\\n\");\r\n\t\r\n\tdelay(2000);\r\n\ttwiReceive(0x51,0,255); \/\/receiving bytes stored in EEPROM in the second example above, second page\r\n\tfor (int i=0;i&lt;255;i++)\tprintMsg(\"%c\",buffer[i]); printMsg(\"\\n\");\r\n}\r\n<\/pre>\n<pre class=\"lang:c++ decode:true \" title=\"wire.c\">\/* Two Wire Interface, I2C (or IIC), here will be called 'twi', and we have\r\n   only twiEnable(), twiSend() and twiReceive(). The twiSend() function is \r\n\t fairly simple, we just send address of the device shifted to the left by\r\n\t 1 bit, or-red | zero (0) at free space that tell I2C bus it is for write operation.\r\n\t The receive twiReceive() function works by sending address also shifted left\r\n\t one bit with logic or | zero (0) at empty bit (LSB), but then we must send command \r\n\t to the device depending what device has. After command, we stop (although\r\n\t we can remove STOP condition and continue to \"repeated start\", then we\r\n\t must change bit after address of the device, now it is one (1) that tells\r\n\t I2C bus we want to read. If we try only read from some address, device\r\n\t don't know what to send. So we must first issue command, then read. For\r\n\t specific command set read datasheet of particular device - it is different\r\n\t for all different devices. More on my website: http:\/\/wp.me\/p7jxwp-nD *\/\r\n\r\n#include \"stm32f10x.h\"\r\n#include \"delayUs.h\"\r\n#include \"wire.h\"\r\n#include \"printMsg.h\"\r\n\r\nvoid twiEnable(void) \r\n{\r\n  \/\/just set all registries, but NOT START condition - execute once in main.c\r\n\tRCC-&gt;APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN; \/\/B port enabled, alternate function \r\n\tRCC-&gt;APB1ENR |= RCC_APB1ENR_I2C1EN; \/\/I2C 1 enabled \r\n\tGPIOB-&gt;CRL = 0xFF000000;\/\/ setting just pins B7 (SDA) and B6 (SCL), while leaving the rest intact 50 MHz!\r\n\tI2C1-&gt;CR2 |= 50; \/\/ GPIO clock freq=50 MHz MUST !!! be equal APB frequency (GPIO, 2, 10 or 50 MHz)\r\n\tI2C1-&gt;CCR |= I2C_CCR_FS; \/\/fast mode\r\n\tI2C1-&gt;CCR |= 30; \/\/not sure for 400 000 - (10= 1.2 MHz, 15=800 kHz, 30=400 kHz)\r\n\tI2C1-&gt;TRISE |= 51; \/\/ maximum rise time is 1000 nS\r\n\tI2C1-&gt;CR1 |= I2C_CR1_PE; \r\n}\r\n\r\nvoid twiScan(void)\r\n{\t\tint a=0; \r\n\t \tfor (uint8_t i=0;i&lt;128;i++)\r\n   {\r\n\t\t\tI2C1-&gt;CR1 |= I2C_CR1_START;\r\n\t\t\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_SB));\r\n\t\t\tI2C1-&gt;DR=(i&lt;&lt;1|0); \r\n\t\t\twhile(!(I2C1-&gt;SR1)|!(I2C1-&gt;SR2)){}; \r\n\t\t\tI2C1-&gt;CR1 |= I2C_CR1_STOP; \r\n\t\t\tdelay(100);\/\/minimum wait time is 40 uS, but for sure, leave it 100 uS\r\n\t\t\ta=(I2C1-&gt;SR1&amp;I2C_SR1_ADDR);\r\n\t\t\tif (a==2)\r\n\t\t {\r\n\t\t\t\tprintMsg(\"Found I2C device at adress 0x%X (hexadecimal), or %d (decimal)\\n\",i,i);\r\n\t\t }\r\n\t }\r\n}\r\n\t\r\n\/* Command or commands, or sending bytes, just the same name of the variable 'command' *\/\r\nvoid twiSend(uint8_t address, uint8_t command, uint8_t length)\r\n{\r\n\tI2C1-&gt;CR1 |= I2C_CR1_START; \/\/START condition \r\n\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_SB));\r\n\tI2C1-&gt;DR=(address&lt;&lt;1|0); \/\/sending address of the device, 0 = sending\r\n  while(!(I2C1-&gt;SR1 &amp; I2C_SR1_ADDR)|!(I2C1-&gt;SR2));\t\t\r\n\tI2C1-&gt;DR=command; \/\/filling data register with byte, if single - command, multiple - command(s) and data\r\n\tfor (uint8_t i=0;i&lt;length;i++)\r\n\t{ \r\n\t\tI2C1-&gt;DR=buffer[i]; \/\/filling buffer with command or data\r\n\t\tdelay(60);\r\n\t}\r\n\tI2C1-&gt;CR1 |= I2C_CR1_STOP;\r\n}\r\n\r\nvoid twiReceive(uint8_t address, uint8_t command, uint8_t length) \r\n{\r\n\tI2C1-&gt;CR1 |= I2C_CR1_ACK;\r\n  I2C1-&gt;CR1 |= I2C_CR1_START; \/\/start pulse \r\n\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_SB));\r\n\tI2C1-&gt;DR=(address&lt;&lt;1|0); \/\/sending address of the device, 0 = sending\r\n\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_ADDR)|!(I2C1-&gt;SR2 &amp; I2C_SR2_BUSY));\r\n\tI2C1-&gt;DR=command; \/\/sending command to the device in order to request data\r\n\tI2C1-&gt;CR1 |= I2C_CR1_START; \/\/REPEATED START condition to change from sending address + command to receive data\r\n\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_SB));\r\n\tI2C1-&gt;DR=(address&lt;&lt;1|1); \/\/sending address of the device, 1 = reading \r\n\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_ADDR)|!(I2C1-&gt;SR2));\r\n\t\r\nif (length==1)  \/\/receiving single byte, N=1\r\n\t{\r\n\t\twhile(!(I2C1-&gt;SR1)|!(I2C1-&gt;SR2));\r\n\t\tI2C1-&gt;CR1 &amp;= ~I2C_CR1_ACK; \/\/this will send later NAK (not acknowledged) to signal it is last byte\r\n\t\tI2C1-&gt;CR1 |= I2C_CR1_STOP; \/\/issuing STOP condition before (!) reading byte\r\n\t\tbuffer[0]=I2C1-&gt;DR; \/\/single byte is read AFTER NAK (!) and STOP condition\r\n\t} \r\n\tif (length==2) \/\/receiving two bytes, N=2\r\n\t{\r\n\t\twhile(!(I2C1-&gt;SR1)|!(I2C1-&gt;SR2));\r\n\t\tI2C1-&gt;CR1 &amp;= ~I2C_CR1_ACK; \/\/this will send later NAK (not acknowledged) before last byte\r\n    I2C1-&gt;CR1 |= I2C_CR1_STOP;\r\n\t\tbuffer[0]=I2C1-&gt;DR; \/\/reading N-1 byte, next to last byte is in DR, last one still in shift register\r\n\t\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_RXNE)|!(I2C1-&gt;SR2));\r\n\t\tbuffer[1]=I2C1-&gt;DR; \/\/read last N byte now available \r\n\t} \r\n  if (length&gt;2) \/\/receiving more than two bytes, N&gt;2\r\n\t{\r\n\t\t\r\n\t  for (uint8_t i=0;i&lt;length;i++)\r\n\t  { \r\n\t\t\t                     \r\n\t\t  if (i&lt;(length-3))      \/\/ if it is not N-2, then read all bytes\r\n\t\t\t{\r\n\t\t\t\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_RXNE)|!(I2C1-&gt;SR2));\r\n\t\t\t\tbuffer[i]=I2C1-&gt;DR;  \r\n\t\t\t}\r\n\t\t  else if (i==length-3) \/\/ if it is N-2 then read \r\n\t\t\t{\r\n\t\t\t\twhile(!(I2C1-&gt;SR1)|!(I2C1-&gt;SR2));\r\n\t\t\t\tbuffer[i]=I2C1-&gt;DR; \r\n\t\t\t\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_RXNE)|!(I2C1-&gt;SR2));\r\n\t\t\t\tI2C1-&gt;CR1 &amp;= ~I2C_CR1_ACK; \/\/this will send later NAK (not acknowledged) before last byte\r\n\t\t\t\tI2C1-&gt;CR1 |= I2C_CR1_STOP;\r\n\t\t\t}\r\n\t    else if (i==length-2) \/\/ if it is N-1 then read\r\n\t\t\t{\r\n\t\t\t\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_RXNE)|!(I2C1-&gt;SR2));\r\n\t\t\t\tbuffer[i]=I2C1-&gt;DR; \r\n\t\t\t}\r\n\t\t\telse if (i==length-1) \/\/ else it is N byte \r\n\t\t\t{\r\n\t\t\t\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_RXNE)|!(I2C1-&gt;SR2)){};\r\n\t\t    buffer[i]=I2C1-&gt;DR;  \r\n\t\t\t}\r\n    } \r\n }\r\n}\r\n<\/pre>\n<pre class=\"lang:c++ decode:true \" title=\"wire.h\">#ifndef wire_h\r\n#define wire_h\r\n\r\n#include &lt;stdint.h&gt;\r\n\r\nextern uint8_t  address, command, length; \r\nextern uint8_t  buffer[];\r\nextern uint8_t  status1;\r\nextern uint8_t  status2;\r\n\r\nvoid twiEnable(void);\r\nvoid twiScan(void);\r\nvoid twiSend(uint8_t address, uint8_t command, uint8_t length);\r\nvoid twiReceive(uint8_t address, uint8_t command, uint8_t length);\r\n\r\n\r\n#endif\r\n<\/pre>\n<pre class=\"lang:c++ decode:true \" title=\"printMsg.c\">#include \"stm32f10x.h\"\r\n#include \"stdint.h\"\r\n#include &lt;stdio.h&gt;\r\n#include \"stdarg.h\"\r\n#include \"string.h\"\r\n#include \"printMsg.h\"\r\n\r\nchar buff[256];\r\n\r\nvoid usart_1_enable(void)\r\n{\r\n  \/\/enabling pin A9 for alternating funct. for uart\/usart\r\n  RCC-&gt;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)\r\n\tGPIOA-&gt;CRH |= GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9_0 | GPIO_CRH_MODE9_1; \/\/port A9\r\n\tGPIOA-&gt;CRH &amp;= ~GPIO_CRH_CNF9_0; \/\/port A9\r\n\tGPIOA-&gt;CRH &amp;= ~(GPIO_CRH_MODE10_0|GPIO_CRH_MODE10_1); \/\/port A10 is RX\r\n\tGPIOA-&gt;CRH |= GPIO_CRH_CNF10_0; \/\/port A10 is RX\r\n\t\r\n\t\/\/GPIOA-&gt;CRH = 0x444444B4; \/\/ A9 is alternate output, 50 MHz, push-pull - not this time short version (!)\r\n\t\/\/clkPer\/(baudRx_16bit)=72MHZ\/9600 = 7500 = 0x1D4C\r\n\t\/* Remove comment line for speed that you want to use*\/\r\n\t\/\/USART1-&gt;BRR = (0xEA60); \/\/   1200 Baud\r\n\t\/\/USART1-&gt;BRR = (0x7530); \/\/   2400 Baud\r\n\t\/\/USART1-&gt;BRR = (0x3A98); \/\/   4800 Baud\r\n\t\/\/USART1-&gt;BRR = (0x1D4C); \/\/   9600 Baud\r\n\t\/\/USART1-&gt;BRR = (0x1388); \/\/  14400 Baud\r\n\t\/\/USART1-&gt;BRR = (0xEA6) ; \/\/  19200 Baud\r\n\t\/\/USART1-&gt;BRR = (0x9c4) ; \/\/  28800 Baud\r\n\t\/\/USART1-&gt;BRR = (0x753) ; \/\/  38400 Baud\r\n\t\/\/USART1-&gt;BRR = (0x505) ; \/\/  56000 Baud\r\n\t\/\/USART1-&gt;BRR = (0x4E2) ; \/\/  57600 Baud\r\n\tUSART1-&gt;BRR = (0x271) ; \/\/ 115200 Baud\r\n\t\/\/USART1-&gt;BRR = (0x232) ; \/\/ 128000 Baud\r\n\t\/\/USART1-&gt;BRR = (0x119) ; \/\/ 256000 Baud\r\n\t\/\/USART1-&gt;BRR = (0x8C)  ; \/\/ 512000 Baud\r\n\t\/\/USART1-&gt;BRR = (0x46)  ; \/\/ 1024000 Baud\r\n\t\/\/USART1-&gt;BRR = (0x23)  ; \/\/ 2048000 Baud\r\n  \/\/USART1-&gt;BRR = (0x18)  ; \/\/ 3000000 Baud (3 MHz, max speed that HTerm can get, non-standard speed)\r\n\t\r\n\t\r\n\tUSART1-&gt;CR1 |= USART_CR1_TE; \/\/transmitter enable\r\n\tUSART1-&gt;CR1 |= USART_CR1_RE; \/\/receiver enable\r\n\tUSART1-&gt;CR1 |= USART_CR1_UE; \/\/usart enable\r\n}\r\n\r\nvoid printMsg(char *msg, ...)\r\n{\r\n\t\/\/char buff[120]; \/\/was 80\r\n\tva_list args;\r\n\tva_start(args,msg); \r\n\tvsprintf(buff,msg,args);\r\n\r\n\tfor(int i=0;i&lt;strlen(buff);i++)\r\n\t{\r\n\t  USART1-&gt;DR = buff[i];\r\n\t  while(!(USART1-&gt;SR &amp; USART_SR_TXE)); \/\/wait for TXE, 1 = data transferred\r\n  }\r\n}\r\n<\/pre>\n<pre class=\"lang:c++ decode:true \" title=\"printMsg.h\">#ifndef printMsg_h\r\n#define printMsg_h\r\n\r\nextern int len;\r\nextern char buff[];\r\n\/\/char buff[];\r\n\r\nvoid usart_1_enable(void);\r\nvoid printMsg(char *msg, ...);\r\n\r\n#endif\r\n<\/pre>\n<pre class=\"lang:c++ decode:true \" title=\"delayUs.c\">void delay(unsigned long cycles)\r\n{\r\n  while(cycles &gt;0)\r\n\t{\r\n\t\tasm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");\r\n\t\tasm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");\r\n\t\tasm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");\r\n\t\tasm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");\r\n\t\tasm(\"nop\");asm(\"nop\");asm(\"nop\");  \/\/to get 1 uS if delay(1)\r\n  cycles--; \r\n\t}\r\n}\r\n<\/pre>\n<pre class=\"lang:c++ decode:true \" title=\"delayUs.h\">#ifndef delayUs_h\r\n#define delayUs_h\r\n\r\nextern void delay(unsigned long cycles);\r\n\r\n#endif\r\n<\/pre>\n<p>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.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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, &hellip; <a href=\"https:\/\/wildlab.org\/index.php\/2019\/03\/02\/stm32-programming-eeprom-over-i2c-bus\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">STM32 write and read EEPROM over I2C bus<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":1779,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts\/1776"}],"collection":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/comments?post=1776"}],"version-history":[{"count":6,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts\/1776\/revisions"}],"predecessor-version":[{"id":2167,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts\/1776\/revisions\/2167"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/media\/1779"}],"wp:attachment":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/media?parent=1776"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/categories?post=1776"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/tags?post=1776"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}