{"id":1465,"date":"2019-02-26T02:36:22","date_gmt":"2019-02-26T01:36:22","guid":{"rendered":"http:\/\/wildlab.org\/?p=1465"},"modified":"2021-06-22T09:24:27","modified_gmt":"2021-06-22T08:24:27","slug":"i2c-barometer-stm32","status":"publish","type":"post","link":"https:\/\/wildlab.org\/index.php\/2019\/02\/26\/i2c-barometer-stm32\/","title":{"rendered":"I2C and MS5611 precision Barometer using STM32"},"content":{"rendered":"<h2>I2C or ISP protocol, what chice?<\/h2>\n<p>Both options are okay, ISP protocol is faster, can run over 40 MHz, but sometimes we have more than one device, so it is better sometimes to use I2C protocol.\u00a0 After all, ADC conversion on this device may take up to 10 mS, so faster protocol will not yield faster reading.<\/p>\n<h2>How to connect <a href=\"https:\/\/www.banggood.com\/MS5611-GY-63-Atmospheric-Pressure-Sensor-Module-IICSPI-Communication-p-965980.html?p=EH10221611330201505Q\" target=\"_blank\" rel=\"noopener noreferrer\">MS5611<\/a> to <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\">STM32<\/a><\/h2>\n<p>First we need to look at schematic diagram to know how to connect <a href=\"https:\/\/www.banggood.com\/MS5611-GY-63-Atmospheric-Pressure-Sensor-Module-IICSPI-Communication-p-965980.html?p=EH10221611330201505Q\" target=\"_blank\" rel=\"noopener noreferrer\">MS5611<\/a> to <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\">STM32 board<\/a>(s). In our case of I2C interfacing, we should look at second part of the picture below. (I included whole datasheet even more below.\u00a0 Note that in the case of I2C, pin CSB play different role than in ISP mode. Leave it unconnected for address 0x77, or connect to Vdd in the case you already have the same device, this time address will be 0x76. 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, SDA pin is connected to GPIO B7 and SCL to GPIO B6. As this page is intended for learning I2C protocol on <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\">STM32<\/a> micro-controllers, I will talk here only about this protocol. SPI will come later, and if you are subscribed to the website, you will get e-mail notification about updates. Back to connections &#8211; pin PS on <a href=\"https:\/\/www.banggood.com\/MS5611-GY-63-Atmospheric-Pressure-Sensor-Module-IICSPI-Communication-p-965980.html?p=EH10221611330201505Q\" target=\"_blank\" rel=\"noopener noreferrer\">MS5611<\/a> must be on Vdd, since it is Protocol Select. In the document it says that it is low voltage device, and 3V is mentioned, but absolute maximum rating is 4V, so if <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\">STM32<\/a> works on 3.3V, that is exactly what we want.<\/p>\n<p><a href=\"https:\/\/wildlab.org\/wp-content\/uploads\/2019\/02\/page13.jpg\"><img loading=\"lazy\" class=\"aligncenter size-full wp-image-1473\" src=\"https:\/\/wildlab.org\/wp-content\/uploads\/2019\/02\/page13.jpg\" alt=\"MS5611\" width=\"597\" height=\"789\" srcset=\"https:\/\/wildlab.org\/wp-content\/uploads\/2019\/02\/page13.jpg 597w, https:\/\/wildlab.org\/wp-content\/uploads\/2019\/02\/page13-227x300.jpg 227w\" sizes=\"(max-width: 597px) 100vw, 597px\" \/><\/a><\/p>\n<p>On the board GY-63, it is confusing where is SDI for SPI protocol. It is the same pin as SDA, but someone forget to add that pin name &#8220;SDA\/SDI&#8221;. In both cases SDI\/SDA gives us data, while SCL or SCLK as is on datasheet has clock. We here need for I2C\u00a0 SDA and SCL only for communication. Vcc (or Vdd in the document) goes to +3.3V, GND to GND of <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\">STM32<\/a>, and PS to Vdd bypass or separate wire to available +3.3V on the <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\">STM32<\/a> board.<\/p>\n<p><a href=\"https:\/\/wildlab.org\/wp-content\/uploads\/2019\/02\/GY-63.jpg\"><img loading=\"lazy\" class=\"aligncenter size-large wp-image-1472\" src=\"https:\/\/wildlab.org\/wp-content\/uploads\/2019\/02\/GY-63-1024x754.jpg\" alt=\"MS5611\" width=\"474\" height=\"349\" srcset=\"https:\/\/wildlab.org\/wp-content\/uploads\/2019\/02\/GY-63-1024x754.jpg 1024w, https:\/\/wildlab.org\/wp-content\/uploads\/2019\/02\/GY-63-300x221.jpg 300w, https:\/\/wildlab.org\/wp-content\/uploads\/2019\/02\/GY-63.jpg 1410w\" sizes=\"(max-width: 474px) 100vw, 474px\" \/><\/a><\/p>\n<p>Here is documentation abut <a href=\"https:\/\/www.banggood.com\/MS5611-GY-63-Atmospheric-Pressure-Sensor-Module-IICSPI-Communication-p-965980.html?p=EH10221611330201505Q\" target=\"_blank\" rel=\"noopener noreferrer\">MS5611<\/a> precision barometer:<\/p>\n<p>[gview file=&#8221;https:\/\/wildlab.org\/wp-content\/uploads\/2019\/02\/MS5611.pdf&#8221;]<\/p>\n<p>For complete work in Keil, we need 7 files, all seven should be in the same directory of the project with name, for example <a href=\"https:\/\/www.banggood.com\/MS5611-GY-63-Atmospheric-Pressure-Sensor-Module-IICSPI-Communication-p-965980.html?p=EH10221611330201505Q\" target=\"_blank\" rel=\"noopener noreferrer\">MS5611<\/a> barometer. It has thermometer too with two digits decimal precision. Your choice of project name, but name of individual component must be as is the name of .c and .h files, since it depends of &#8216;#include&#8217; statements.\u00a0 Here I will give you all 7 files, of which first is barometer.c (containing main() function), wire.c, wire.h, delayUs.c, delayUs.h, printMsg.c, printMsg.h . All MCU programming is done using <a href=\"https:\/\/www.banggood.com\/3_3V-5V-XTW-ST-LINK-V2-STM8-STM32-Simulator-Programmer-Downloader-Debugger-With-20cm-Dupont-Wire-p-1177014.html?p=EH10221611330201505Q\" target=\"_blank\" rel=\"noopener noreferrer\">ST-link dongle<\/a>. For data transfer to the computer, you need <a href=\"https:\/\/www.banggood.com\/FT232RL-FTDI-USB-To-TTL-Serial-Converter-Adapter-Module-For-Arduino-p-917226.html?p=EH10221611330201505Q\" target=\"_blank\" rel=\"noopener noreferrer\">FTDI usb dongle<\/a>.<\/p>\n<p>barometer.c file:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\/* More on my website: http:\/\/wp.me\/p7jxwp-nD *\/\r\n\r\n#include \"stm32f10x.h\"\r\n#include \"printMsg.h\"\r\n#include \"delayUs.h\"\r\n#include \"wire.h\"\r\n#include \"math.h\"\r\n\r\nuint8_t  buffer[5];  \/\/twi buffer, for now 32 elements, for other purpose (e.g. for OLED it is 1024) change as you wish\r\nuint8_t  address=0x77, length=0;\r\nuint16_t calibrationData[7];\r\nint64_t OFF, OFF2, SENS, SENS2;\r\nint pressure, D1, D2, dT, P, Pa, TEMP, T2;\r\n\r\nvoid setupSensor(void)\r\n{\r\n\ttwiSend(address, 0x1E,1); \/\/just send 1 byte that tells MS5611 to reset\r\n\tdelay(20000); \/\/delay 10 mS needed for device to execute reset\r\n\tfor (int i=1;i&lt;=6;i++)\r\n\t{\r\n\ttwiReceive(address, 0xA0+i*2, 2); \/\/read all 14 bytes for callibration data from PROM\r\n\t\/\/printMsg(\"b0= 0x%x, b1= 0x%x, b2= 0x%x \\n\",buffer[0], buffer[1], buffer[2]); \/\/for debug purposes\r\n  delay(50); \/\/at least 40 uS\r\n\tcalibrationData[i] = buffer[0]&lt;&lt;8|buffer[1]; \/\/pair of bytes goes into each element of callibrationData[i], global variables, 14 uint8_t into 7 uint16_t\r\n  }\r\n\t\/\/delay(50);\r\n\t\/\/printMsg(\"b0= %d, b1= %d, b2= %d \\n\",buffer[0], buffer[1], buffer[2]); \/\/for debug purposes\r\n}\r\n\r\nint getPressure(void)\r\n{\r\n\tD1=0;D2=0;\r\n\ttwiSend(address, 0x48,1); \/\/set D1 OSR=4096 (overscan, maximum) 0x48\r\n\tdelay(25000);\/\/must be 15 mS or more\r\n\ttwiReceive(address, 0x00, 3); \/\/initiate and read ADC data, 3 bytes\r\n\t\/\/printMsg(\"b0= 0x%x, b1= 0x%x, b2= 0x%x ===========\\n\",buffer[0], buffer[1], buffer[2]); \/\/for debug purposes\r\n\tD1 = D1&lt;&lt;8 | buffer[0]; \/\/shifting first MSB byte left\r\n\tD1 = D1&lt;&lt;8 | buffer[1]; \/\/another byte\r\n\tD1 = D1&lt;&lt;8 | buffer[2]; \/\/LSB byte last\r\n\ttwiSend(address, 0x58,1); \/\/set D2 OSR=4096 (overscan, maximum) 0x58\r\n\tdelay(25000); \/\/must be 15 mS or more\r\n\ttwiReceive(address, 0x00, 3); \/\/initiate and read ADC data, 3 bytes\r\n\tD2 = D2&lt;&lt;8 | buffer[0]; \/\/shifting first MSB byte left\r\n\tD2 = D2&lt;&lt;8 | buffer[1]; \/\/another byte\r\n\tD2 = D2&lt;&lt;8 | buffer[2]; \/\/LSB byte last\r\n\t\t\r\n\tdT = D2 - ((int)calibrationData[5] &lt;&lt; 8);\r\n  TEMP = (2000 + (((int64_t)dT * (int64_t)calibrationData[6]) &gt;&gt; 23)); \/\/temperature before second order compensation\r\n  if (TEMP&lt;2000)  \/\/if temperature of the sensor goes below 20\u00b0C, it activates \"second order temperature compensation\"\r\n    {\r\n      T2=pow(dT,2)\/2147483648;\r\n      OFF2=5*pow((TEMP-2000),2)\/2;\r\n      SENS2=5*pow((TEMP-2000),2)\/4;\r\n      if (TEMP&lt;-1500) \/\/if temperature of the sensor goes even lower, below -15\u00b0C, then additional math is utilized\r\n        {\r\n          OFF2=OFF2+7*pow((TEMP+1500),2);\r\n          SENS2=SENS2+11*pow((TEMP+1500),2)\/2;\r\n        }\r\n    }\r\n    else \r\n      {\r\n          T2=0;\r\n          OFF2=0;\r\n          SENS2=0;\r\n      }\r\n  TEMP = ((2000 + (((int64_t)dT * (int64_t)calibrationData[6]) &gt;&gt; 23))-T2); \/\/second order compensation included\r\n  OFF = (((unsigned int)calibrationData[2] &lt;&lt; 16) + (((int64_t)calibrationData[4] * dT) &gt;&gt; 7)-OFF2); \/\/second order compensation included\r\n  SENS = (((unsigned int)calibrationData[1] &lt;&lt; 15) + (((int64_t)calibrationData[3] * dT) &gt;&gt; 8)-SENS2); \/\/second order compensation included\r\n  P = (((D1 * SENS) &gt;&gt; 21) - OFF) &gt;&gt; 15; \r\n  return P; \/\/returns back pressure P\r\n}\r\n\r\nint main(void)\r\n{\r\n\/\/In new (2019\/03\/01) added twiScan(); function. You may use it to find your I2C device. Just remove \/\/ coment characters.\r\n          \/\/twiScan();\r\n\t  RCC-&gt;APB2ENR |= RCC_APB2ENR_IOPCEN;\r\n\t  GPIOC-&gt;CRH = 0x00300000;\r\n    GPIOC-&gt;BSRR = GPIO_BSRR_BR13; \/\/let's lit blue LED \r\n\t  RCC-&gt;APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN;\r\n\t  usart_1_enable();\r\n\t  twiEnable();\r\n\t  setupSensor(); \/\/set essential things\r\n\t  \/\/printMsg(\"works? or stuck somewhere?\"); \/\/debug mode :)\r\n\twhile(1)\r\n\t{\t\r\n    pressure = getPressure(); \/\/get pressure and temperature, calculate pressure offset with callibration data and temperature\r\n\t  printMsg(\"Temperature of the sensor= %.2f, barometric pressure= %.2f\\n\", (float)TEMP\/100, (float)pressure\/100);\t\r\n\t\tdelay(2000000); \/\/every 2 seconds it send data over the USART1 (equivalent of Serial.print on Arduino)\r\n\t\t\/\/for(;;); \/\/execute once, just for debug purpose\r\n\t}\r\n}\r\n<\/pre>\n<p>wire.c file:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"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);\/\/minium 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<p>wire.h file:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">#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 uint16_t calibrationData[];\r\nextern uint8_t  buffer[];\r\nextern uint8_t  status1;\r\nextern uint8_t  status2;\r\n\r\nvoid twiEnable(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<p>delayUs.c file:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"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<p>delayUs.h file:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">#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>printMsg.c file:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">#include \"stm32f10x.h\"\r\n#include \"stdint.h\"\r\n#include &lt;stdio.h&gt;\r\n#include &lt;stdlib.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<p>printMsg.h file:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">#ifndef printMsg_h\r\n#define printMsg_h\r\n\r\nextern int len;\r\nextern char buff[];\r\n\r\nvoid usart_1_enable(void);\r\nvoid printMsg(char *msg, ...);\r\n\r\n#endif\r\n<\/pre>\n<p>Here is the video:<\/p>\n<p><iframe loading=\"lazy\" title=\"STM32 programming I2C and MS5611 barometer example in Keil\" width=\"474\" height=\"267\" src=\"https:\/\/www.youtube.com\/embed\/qhLJ8VVbQjo?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<h2>Some related documents<\/h2>\n<p>[gview file=&#8221;https:\/\/wildlab.org\/wp-content\/uploads\/2019\/02\/Understanding_I2C.pdf&#8221;]<\/p>\n<p><a href=\"https:\/\/www.st.com\/content\/ccc\/resource\/technical\/document\/reference_manual\/59\/b9\/ba\/7f\/11\/af\/43\/d5\/CD00171190.pdf\/files\/CD00171190.pdf\/jcr:content\/translations\/en.CD00171190.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">External link for STM32f10x series.<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I2C or ISP protocol, what chice? Both options are okay, ISP protocol is faster, can run over 40 MHz, but sometimes we have more than one device, so it is better sometimes to use I2C protocol.\u00a0 After all, ADC conversion on this device may take up to 10 mS, so faster protocol will not yield &hellip; <a href=\"https:\/\/wildlab.org\/index.php\/2019\/02\/26\/i2c-barometer-stm32\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">I2C and MS5611 precision Barometer using STM32<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":1466,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[89,88,141,143],"_links":{"self":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts\/1465"}],"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=1465"}],"version-history":[{"count":42,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts\/1465\/revisions"}],"predecessor-version":[{"id":3823,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts\/1465\/revisions\/3823"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/media\/1466"}],"wp:attachment":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/media?parent=1465"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/categories?post=1465"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/tags?post=1465"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}