I2C scanner for STM32f10x series
I2C scanner is fairly simple, yet fast and effective way to find whatever device you put onto I2C bus. Some devices (boards) comes with clear designation on the PCB, some with misleading designation – address is shifted to the left, so for example OLED display typical address is 0x3C/0x3D (depends of address selector jumper), but shifted to the left by 1 bit is 0x78 or 0x7A respectively. They made it as if zero bit is inserted at LSB place so that you ‘just’ send it as-is. But that is bad practice. Rather make your own shifting routine and insert 1 or 0, whether you want to read or write at this address.
Example:
/* Part of the code, will not work alone !!! */ 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);//minium 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); } } }
Whole thing scan all 127 addresses in 45 mS.
Here is how looks whole packet with six addresses found. After each address found, there is 5.5 mS delay until timeout.:
Almost standard wire.c library
Since I am beginner, I just begin to make few more or less standard libraries for STM32f10x series. Right now there are three functions in wire.c; twiScan(), twiSend(), and twiReceive(). So far I did not found need for anything else, but that may change, so stay tuned. Since I have none of other MCU that has different set of registers with also different syntax for those. Here is complete thing with example of main.c code that does nothing except scan.:
#include "stm32f10x.h" #include "printMsg.h" #include "wire.h" #include "delayUs.h" int main() { usart_1_enable(); //enabling printMsg(); twiEnable();//enabling two wire interface, IIC or I2C twiScan();//before anything else, lets check which devices are on the bus, }
Implemented in wire.c with wireSend() and wireReceive() functions:
/* 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);//minium 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
That is all. Happy scanning! 😀