'전체 글'에 해당되는 글 49건

  1. 2019.12.30 [STM32] HAL을 사용한 I2C LCD 제어

예전에 아는 후배한테 받았던 1602 LCD 가 있다. PCF8574 I2C expander ic가 달려있는 모듈 이었는데 GPIO를 이용하지 않고 I2C를 이용하여 LCD를 제어해야 했기 때문에 오랬동안 신경도 쓰지 않았다.

최근에 STM32 HAL을 사용하게 된 후로 AVR에서 했던 자세한 레지스터 설정을 간단한 HAL코드로 구현이 가능하고 ADC, I2C등 다른 기능들을 사용하기 쉬워 I2C를 이용한 LCD 제어를 해봤다.

 

PCF8574가 붙어있는 1602lcd

 

1602 LCD timing diagram

Write mode timing diagram
Read mode timing diagram

1602 character LCD에 사용되는 timing diagram 이다. 4bit mode에서는 데이터를 전송할때 앞 4bit를 먼저 보내고 뒤 4bit를 다시 그 후에 바로 보낸다는 점이 다르다.

 

1602 LCD 4 bit interface

LCD에 처음 전원을 넣고 초기화하는 과정이다.

 

HAL 코드

1
2
3
4
5
6
7
#define SLAVE_LCD_ADDRESS 0x38<<1 // i2c slave address
 
#define SBI(address, ab) (address |= (0x01<<ab))
#define CBI(address, ab) (address &= ~(0x01<<ab))
#define RS 0
#define RW 1
#define E 2

I2C통신을 하기위해 slave address와 RS, RW, Enalbe 핀 번호들을 정의했다.  SBI는 ab위치의 bit를 1, CBI는 ab위치의 bit를 0으로 만들때 쓴다.

slave address가 0x38인 이유는 후배가 준 LCD가 문제가 있어서 따로 PCF8574A ic와 LCD를 사용하여 테스트 했기 때문이다.

 

1
2
3
4
5
6
7
8
9
10
11
uint8_t LCD_BF(){
    uint8_t val=0;
    uint8_t Tdata[2]={0b00000010, 0b00000110};
 
    HAL_I2C_Master_Transmit(&hi2c1,SLAVE_LCD_ADDRESS,Tdata,2,10);
    HAL_I2C_Master_Receive(&hi2c1,SLAVE_LCD_ADDRESS,&val,1,10);
    //HAL_I2C_Master_Transmit(&hi2c1,SLAVE_LCD_ADDRESS,&Tdata[2],1,10);
 
    return val;
}

LCD에 데이터를 입력하기전 LCD가 현재 작업중인지 아닌지 BF를 확인하기 위해 필요한 코드이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void LCD_4bit_Write(uint8_t rs, uint8_t data){
    uint8_t Tdata[2]={0,0};
    while(LCD_BF()&0x80){}// lcd_bf 0b10000000 일시 무한 루프
 
    if(rs)
        SBI(Tdata[0], RS);
    else
        CBI(Tdata[0], RS);
    CBI(Tdata[0], RW);
 
    Tdata[1]=Tdata[0]=(data&0b11110000)|(Tdata[0]&0b00001111);
    SBI(Tdata[1],E);
    HAL_I2C_Master_Transmit(&hi2c1,SLAVE_LCD_ADDRESS,Tdata,2,100);
 
    Tdata[1]=Tdata[0]=((data<<4)&0b11110000)|(Tdata[0]&0b00001111);
    SBI(Tdata[1],E);
    HAL_I2C_Master_Transmit(&hi2c1,SLAVE_LCD_ADDRESS,Tdata,2,100);
 
    Tdata[0]=0;
    HAL_I2C_Master_Transmit(&hi2c1,SLAVE_LCD_ADDRESS,Tdata,1,100);
}

4 bit mode LCD에 데이터를 쓰거나 명령을 쓸때 사용한다. rs가 0이면 명령을 쓰고, 1이면 데이터를 쓴다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void LCD_init(){
 
    uint8_t Tdata[2]={0x30,0x34};
    HAL_Delay(20);
    HAL_I2C_Master_Transmit(&hi2c1,SLAVE_LCD_ADDRESS,Tdata,2,10);
    HAL_Delay(5);
    HAL_I2C_Master_Transmit(&hi2c1,SLAVE_LCD_ADDRESS,Tdata,2,10);
    HAL_Delay(1);
    HAL_I2C_Master_Transmit(&hi2c1,SLAVE_LCD_ADDRESS,Tdata,2,10);
    HAL_Delay(1);
    Tdata[0]=0x20;
    Tdata[1]=0x24;
    HAL_I2C_Master_Transmit(&hi2c1,SLAVE_LCD_ADDRESS,Tdata,2 ,10);
    HAL_Delay(1);
    LCD_4bit_Write(0,0b00101000);    //function set //
    
    LCD_4bit_Write(0,0b00001000);    //display off
  
    LCD_4bit_Write(0,0b00000001);    //clear display
  
    LCD_4bit_Write(0,0b00000010);    //return home
  
    LCD_4bit_Write(0,0b00000110);    //entry mode set
 
    LCD_4bit_Write(0,0b00001100);    //display on    //cursor off*/
 
 
}

4 bit mode 초기화를 하기 위해 사용한다.

 

1
2
3
4
5
6
7
void Write_str(uint8_t *str,int length){
    for(int i=0;i<length;i++){
        if(str[i]==NULL)
            str[i]=' ';
        LCD_4bit_Write(1,str[i]);
    }
}

LCD에 문자열을 쓰기 위해 사용한다. sprintf() 함수를 사용하여 str 버퍼에 문자열을 집어 넣고 LCD에 출력할 때 사용한다.

HAL I2C LCD 결과

mpu6050 raw data를 보여주는 모습

핀을 VCC GND를 제외하고 8 bit GPIO핀을 사용했을경우 11개의 핀을 사용하던 것을 I2C SCL, SDA 2개의 핀으로 작동시킬 수 있다.

작동은 되지만 살짝 불안정한 모습을 보일때가 있다. 또한 GPIO를 사용한게 아닌 I2C를 사용했기 때문에 통신 시간에 의해 시간이 좀 걸리는 편이다.

 

첫 글인 만큼 중구난방으로 글을 쓴 것 같다. STM32 HAL과 I2C를 이용한 LCD 제어 정보를 구글에 검색해도 아두이노 제어 혹은 I2C가 아닌 GPIO를 사용한 코드들이 너무 많아 원하는 결과를 찾기 힘들었다. 그래서 나처럼 고생할 누군가에게 약간이라도 도움이 됐으면 하는 마음으로 글을 썼다.

 

 

Posted by DDTXRX
,