2021-05-04 04:37 AM
Hi,
i come up with my question related to I2C communication using stm32F103,
I would like to interface my I2C LCD (slave) with MCU ( master), but i have a problem with the Transmit sequence
i have configured the GPIO's PB6 (SCL) and PB7(SDA) as open drain AF, then intialized the I2C,
I have also followed the transmit sequence as described in the datasheet
- Send start condition
- Check if the start condition is sent , if yes clear the SB flag by reading SR1 register (EV5).
- Transmit the slave adress ( 0x3F for LCD HDD44780 + pcf8574 ).
- Wait for ADDR flag in status register, if there was a match for this
slave address, clear flag by reading SR1 followed by teading SR2 (
EV6)
- check if TXE is empty then send the data to data register (EV8_1)
- Check BTF flag
this is the related code below
This is the sda and SCL traces captured with the logic analyzer. RXD--> SDA / DTR--> ClK
My questions are :
1) When i'm using the check condition : while (!(I2C1->SR1 & I2C_SR1_ADDR)) --> check if ADDR matched the slave
or while(!(I2C1->SR1 & I2C_SR1_TXE)) .
the SCL is stretched low permanently and SDA is pulled high, it seems that the master ( MCU) is blocked into these events ( EV6 or EV8_1).
but when i'm using if ((I2C1->SR1 & I2C_SR1_ADDR) ==0) with if, the problem get resolved and i can see the traces.
2) i cannot see the data that i have sent in the traces : as described in the main function I2C_send ( 0x3F, 0x55)
3) The LCD slave adress is 0x3F, but when i disconnect the LCD , i still receive the ACK ( Read and Write) as shown in the picture , is it normal ?
Thank you !
#include "stm32f10x.h" // Device header
#include "stdint.h"
#include <stdio.h>
void GPIO_Init(void);
void I2C1_Init(void);
void LCD_Init (void);
void DelayTimerUs1(int n);
void I2C_send (uint8_t slave_addr, uint8_t data);
void LCD_cmd(char cmd);
void LCD_Data( char Data);
void GPIO_Init(void)
{
// Port B is used for I2C1 communication :
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
GPIOB->CRL &=~ (GPIO_CRL_MODE6_1 | GPIO_CRL_MODE7_1);
GPIOB->CRL |= GPIO_CRL_MODE6_0 | GPIO_CRL_MODE7_0;
GPIOB->CRL |= GPIO_CRL_CNF6_0 | GPIO_CRL_CNF7_0;
GPIOB->CRL |= GPIO_CRL_CNF6_1 | GPIO_CRL_CNF7_1;
}
void I2C1_Init(void)
{
I2C1->CR1 |= I2C_CR1_SWRST;
I2C1->CR1 &=~ I2C_CR1_SWRST;
I2C1->CR2 |= 36;
I2C1->CCR |= 180; // 100Khz
I2C1->TRISE |= 37;
I2C1->CR1 |= I2C_CR1_PE;
}
void I2C_send (uint8_t slave_addr, uint8_t data) // data can be either a command or Data
{
uint16_t temp2, temp0;
//while (I2C1->SR2& I2C_SR2_BUSY){};
if ((I2C1->SR2 & I2C_SR2_BUSY) == 1 ) {}
I2C1->CR1 |= I2C_CR1_ACK;
I2C1->CR1 |= I2C_CR1_START;
// 2) Check START condtion send
while (!(I2C1->SR1 & I2C_SR1_SB)) {}
temp2 = I2C1->SR1; // EV5 if the SB flag is not 1, do nothing,this means that the START //condtion was not sent yet
// 3) Transmit the slave addresse
I2C1->DR = slave_addr; // 0x3F
//while (!(I2C1->SR1 & I2C_SR1_ADDR)) // wait for ADDR flag in status register, if there was a match for this slave address, clear flag
if ((I2C1->SR1 & I2C_SR1_ADDR) ==0) {}
temp0 = I2C1->SR1 | I2C1->SR2; // EV6 clear ADDR flag by reading SR2
// 4) Send the Data to data_register
//while(!(I2C1->SR1 & I2C_SR1_TXE)) {}
if ((I2C1->SR1 & I2C_SR1_TXE) == 0) {}
I2C1->DR = data;
if ( (I2C1->SR1 & I2C_SR1_BTF)==0){}
// while (! (I2C1->SR1 & I2C_SR1_BTF));
//I2C1->CR1 = I2C_CR1_STOP; // send another start condition if we have another //byte
}
void LCD_cmd(char cmd){
uint8_t CMD_H = (cmd & 0xF0); // Isoler Bit poids Fort de la cmd avec le masque 0x0F
uint8_t CMD_Een = (CMD_H |0x0C); // Parametrer bit de config(poid faible), RE=1; E=1 ;
RW=0; RS=0
uint8_t CMD_Edis = (CMD_H |0x08); // RE=1; E=0 ; RW=0; RS=0
uint8_t CMD_Shift = ((cmd<<4) & 0xF0);
I2C_send (0x3F, CMD_Een); // envoi de la cmd avec E=1
I2C_send (0x3F, CMD_Edis); // envoi de la cmd avec E=0;
I2C_send (0x3F, CMD_Shift | 0x0C); // envoi cmd decalée (4bits de poids faible) avec E=1
I2C_send (0x3F, CMD_Shift | 0x08); // envoi cmd decalée (4bits de poids faible) avec E=0
}
void LCD_Data( char Data){
uint8_t data_H = (Data & 0xF0); // Isoler Bit poids Fort de la donnée avec le masque 0x0F
uint8_t data_Een = (data_H |0x0D); // Parametrer bit de config(poid faible), RE=1; E=1 ; RW=0; RS=1
uint8_t data_Edis = (data_H |0x09); // RE=1; E=0 ; RW=0; RS=1
uint8_t data_Shift = ((Data<<4) & 0xF0);
I2C_send (0x3F, data_Een); // envoi de la cmd avec E=1
I2C_send (0x3F, data_Edis); // envoi de la cmd avec E=0;
I2C_send (0x3F, data_Shift | 0x0D); // envoi data decalée avec E=1
I2C_send (0x3F, data_Shift | 0x09); // envoi data decalée avec E=0
}
void DelayTimerUs1(int n){
// 1 Mhz ---> 1Us
//ARR = n (n=1 --> 1us) (n=2 --> 2us) ....
// n = 30000 for 30ms delay
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
TIM2->PSC = 36;
TIM2->ARR = n;
TIM2->CNT = 0;
TIM2->CR1 = TIM_CR1_CEN;
for( int i =0; i<n; i++){
while (!(TIM2->SR & (1<<0))) {}
}
TIM2->SR &=~ (1<<0);
TIM2->CR1 &=~ TIM_CR1_CEN;
}
void LCD_Init (void)
{
GPIO_Init();
DelayTimerUs1(10000); // 10 ms delay
LCD_cmd(0x30);
DelayTimerUs1(10000);
LCD_cmd(0x30);
DelayTimerUs1(10000);
LCD_cmd(0x30);
DelayTimerUs1(10000);
LCD_cmd (0x28); // 0x20 tells the lcd controller that we want to communicate in 4-bit mode with 2 lines Row
LCD_cmd (0x02); // Home (move cursor to top/left character position)
//LCD_cmd (0x14); // Move cursor one character right
}
int main (void){
GPIO_Init();
I2C1_Init();
LCD_Init();
while(1)
{
I2C_send(0x3F, 0x55);
LCD_Data('s');
}
}