-
FND는 7세그먼트의 다른 이름으로 메인칩 + 필요한 최소한의 회로를 모듈이라 한다. 칩 이름 : TM74HC595
이 모듈의 뒷편을 보면 칩 두개가 결합되어있다.
전에 설명한거처럼 STM32 - LCD 드라이버 - 모듈 이렇게 연결되어있다.
LCD드라이버를 사용하는 이유는 모듈이 4개있다면 32개의 GPIO를 전부 연결해야되기 때문이다.
먼저 중간에 연결해주는 LCD 드라이버의 이름은 TM74HC595이다.
SPI는 기본적으로 클럭과 DI가 있는데 클럭은 위에서 아래 한번 갔다오는게 한 주기이다.
만약 DI가 High를 보내고싶으면 아래 그림처럼 위로 올리고, Low를 보내고싶으면 0으로 보낸다.
내 모듈에 맞게 사용하기 위해 위 코드를 변형하면 다음과 같다.
여기서 if문을 보면 0x80은 2진수로 변환하면 1000 0000이다. 8번째 비트가 1인지 아닌지를 확인하는 것이다.
만약 if문에서 x가 52가 대입되었다고 가정하자. 52는 10진수이므로 2진수로 바로 바꾸면 00110100이다.
00110100과 10000000을 &연산하게되면 0000 0000가 된다.
이러면 else 문이 실행되어 Low상태가 된다.
그러고 x를 한칸 시프트 연산을 하게되면 00110100 -> 01101000이 된다.
0110 1000과 1000 0000을 &연산하면 1000 0000이 된ㅠ 다. 최상위 비트가 1이기 때문에 if문으로 간다.
이 과정을 for문만큼인 8번 반복하면 x가 52일때는 Low -> Low -> H igh -> High -> Low -> High -> Low -> Low가 된다.
그리고 뒤 클럭 쪽 코드를 보면 Low -> High -> Low -> High가 반복되며 최종적으로 High에서 끝난다.
위 코드를 그림으로 그리면 WritePin이 High로 끝나기때문에 클럭은 High로 시작한다. 4비트를 예로들어 1001을 보낼거라면 위 그림과 같이 비트가 전송된다.
만약 7을 send함수에 넣게되면 send(0xF8)이 들어간다. 노란색이 클럭이고 파란선이 데이터 선이다.
F8은 2진수로 1111 1000이다. 파형을 보게되면 상승엣지일때 1 1 1 1 1 0 0 0이 보여진다.
소프트웨어적으로 만들면 파형 간격이 일정하지 않을수도 있다. 그러나 세그먼트는 내가 원하는 7을 보여주지않는다.
void send_port(uint8_t X, uint8_t port) { send(X); send(port); HAL_GPIO_WritePin(FND_RCLK_GPIO_Port, FND_RCLK_Pin, LOW); HAL_GPIO_WritePin(FND_RCLK_GPIO_Port, FND_RCLK_Pin, HIGH); }
위 send_port 함수를 보면 X에 0xF8을 넣어보자. 밑에 port에는 0b0001이 들어간다. 여기서 0 0 0 1이렇게 돼있는데, 1의 위치를 보면된다. 1이 네번째 자리에있으면 세그먼트 4번째 자리에 숫자가 켜진다. ob1000을 사용하면 첫번째 자리에 숫자가 나타난다.
정리하면, 첫번째 send(X)에는 내가 쓰고싶은 데이터, port는 어디에 킬지, 클락 Low -> High 동작한다.
void init_fnd(){ _LED_0F[0] = 0xC0; //0 _LED_0F[1] = 0xF9; //1 _LED_0F[2] = 0xA4; //2 _LED_0F[3] = 0xB0; //3 _LED_0F[4] = 0x99; //4 _LED_0F[5] = 0x92; //5 _LED_0F[6] = 0x82; //6 _LED_0F[7] = 0xF8; //7 _LED_0F[8] = 0x80; //8 _LED_0F[9] = 0x90; //9 _LED_0F[10] = 0x88; //A _LED_0F[11] = 0x83; //b _LED_0F[12] = 0xC6; //C _LED_0F[13] = 0xA1; //d _LED_0F[14] = 0x86; //E _LED_0F[15] = 0x8E; //F _LED_0F[16] = 0xC2; //G _LED_0F[17] = 0x89; //H _LED_0F[18] = 0xF9; //I _LED_0F[19] = 0xF1; //J _LED_0F[20] = 0xC3; //L _LED_0F[21] = 0xA9; //n _LED_0F[22] = 0xC0; //O _LED_0F[23] = 0x8C; //P _LED_0F[24] = 0x98; //q _LED_0F[25] = 0x92; //S _LED_0F[26] = 0xC1; //U _LED_0F[27] = 0x91; //Y _LED_0F[28] = 0xFE; //hight - }
이 숫자들이 의미하는건 뭘까?
세그먼트는 총 8개로 이루어져있는데
send_port(0b11111110, 0b0001);
아래 8자리 중 0으로 채워진 부분만 불이 들어온다.
위 코드는 0번에만 불이들어온다.
11111101이면 1자리에만 불이 들어온다.
그럼 E를 출력하고 싶으면 Ex86 -> 134 -> 1000 0110이 된다. 그러면 0,3,4,5,6의 자리에 불이 켜지고, 그것이 E이다.
다음으로 digit4_show() 함수를 분석할 것이다.
void digit4_show(int n, int replay, uint8_t showZero) { int n1, n2, n3, n4; n1 = (int) n % 10; n2 = (int) ((n % 100)) / 10; n3 = (int) ((n % 1000)) / 100; n4 = (int) ((n % 10000)) / 1000; for(int i = 0; i<=replay; i++){ send_port(_LED_0F[n1], 0b0001); if(showZero | n>9)send_port(_LED_0F[n2], 0b0010); if(showZero | n>99)send_port(_LED_0F[n3], 0b0100); if(showZero | n>999)send_port(_LED_0F[n4], 0b1000); } }
예를 들어, 789라는 숫자를 세그먼트에 나타내고싶으면, n = 789이다.
n1은 789 % 10의 결과인 9가 들어간다. n2는 8이 들어가고, n3는 7, n4는 7이 들어간다.
showZero가 1이면 모든 자리 수를 표시하고, showZero가 0이면 789라면 789만 표시된다.