본문 바로가기
임베디드

[오제이 튜브의 임베디드 강의] 19강. FND제어(실무 노하우)

by 덤더리덤떰 2025. 1. 24.

1. 오늘의 목표

: 버튼을 누를 때마다 FND 세그먼트의 숫자가 계속 증가하도록 출력하기 

 

1-1. 세그먼트의 Pin 보는 방법

 

 

  • VCC : VCC와 GND가 연결되어야 FND LED가 켜질 것
  • SCLK : 이 클럭을 기준으로 DO로 데이터가 들어와서 FND모듈이 이 데이터를 2진수로 변환하여 읽어서 숫자를 FND에 출력하게 됨 
  • DO(Data Output) : FND가 어떤 정보를 보냄
    *DI(Data Input) : FND 모듈이 정보를 받음
    => 이때 FND 세그먼트의 특징은 내가 원하는 숫자나 데이터를 보여주는 모듈이기에 데이터를 보낼 일은 없음

 

1-2. FND

  •  7세그먼트의 다른 이름
  • 메인칩 + 메인칩을 사용하는데 필요한 최소한의 회로 = 모듈 (=쪽보드)
  • 뒷면에 칩 2개가 존재

FND 모듈의 뒷면

=> 메인칩(TM74HC595) + 7세그먼트 합친 모듈 = FND 

 

1-3. 7 세그먼트의 구성

 

  • 이 모든 부분부분이 LED이므로 총 소수점 표시(.)까지 8개의 LED 존재 
    => 이때 소수점은 제외해서 총 7개이므로 7 세그먼트라고 부름 
  • 만약 이 LED 키기 위해 3.3V가 필요하다면 원하는 부분에 3.3V 공급하면 그 부분에만 불이 들어오게됨 
  • 메인칩 없이 LED를 제어하고 싶다면 GPIO를 이용해서 제어방법도 존재 

 

 

 


 

 

  • 원래는 메인 칩(STM32)와 FND의 LED 부분부분을 직접 연결해야함
  • 이때, 너무 많아지기에 중간에 LCD 드라이버를 이용해서 LCD드라이버와 FND 모듈에 LED 8개 연결 
  • 최종적으로 STM32와 LCD 드라이버가 통신하여 FND 제어 
    * LCD 드라이버 : TM74HC595

 

1-4. TM74HC595의 특징

 

빨강색 : FND, 노랑색 : LCD 드라이버


 

 

  • QA~QH까지 8개 존재
  • STM32 - TM74HC595 - FND에서  TM-FND 사이의 다리들이 QA~QH임
  • 연결 구성을 보는 방법은 모듈 데이터 시트 OR 테스터기로 쇼트테스트 OR 남이 정리해놓은 것 OR 샘플 코드 구해서 일단 동작시키고 그다음에 추리 

 

 

 

 

 

 

 

2. FND 본격적인 제어 

2-1. SPI통신 

  • SPI에는 CLK(클럭), DI(데이터), CS가 존재하는데 이때 CS 없는 경우 존재 
    => 내가 데이터를 보내고 싶다면 CLK 주기에 맞춰서 데이터 전송 

 

2-2. 회로도 구성하기 

  • 수컷-암컷 빨강/검정 점퍼선 이용해 각각 빵판의 +,-와  FND의 VCC와 GND에 연결 
  • 암컷-암컷 점퍼선 이용해서 SCLK-PB15/ RCLK-PB13/ DIO-PB14 연결 

2-3. PB15/14/13 설정 

    • STM32 칩 자체에서 SPI 통신 지원
      => DMA 이용 가능 (메모리에서 데이터 바로 읽어오는 기능) 
    • 우리는 GPIO 3개를 통해 우리가 직접 클럭을 만들고, 데이터 읽어오는 것 / 쓰는 것 모두 직접 작성하기 
    • PB13/14/15 : GPIO Output으로 설정

PB15/ PB14 / PB13 설정

 

2-4. 샘플 코드 수행해보기 

  • fnd_controller.c와 fnd_controller.h 헤더파일 생성해서 샘플코드 작성
  • 핀과 그룹들이 main.h에 정의되어있기에 헤더파일에 #include "main.h" 해줄 것 

main.h 헤더파일

 

(1) 첫번째 send 함수 

기존의 C++코드를 오른쪽의 코드로 최적화하여 작성 

 

  • 데이터가 오면 X & 0x80 연산 수행 
    <=> X & 1000 0000 == 1이면 HIGH 쓰고 아니면 LOW로 씀 
    <=> 우리가 원하는 데이터 X를 8bit로 넣어서 0x80과 & 연산 후 if~else문 수행 후 해당 X를 왼쪽으로 1칸 shift연산 
  • 결국, 내가 보내는 8bit 데이터 1bit마다 &연산해서 HIGH 와 LOW결정
    ex) 1010 0011 보내면 & 연산과 << 1 연산으로 인해 결론적으로 H L H L L L H H 로 결정
  • 맨 아래 두 줄 코드는 클럭 생성
    => LOW -> HIGH로 내리고 올리면서 하나의 클럭을 생성해주기에 if~else문으로 DIO를 HIGH/LOW 중 하나 쓴 상태에서 클럭이 생성되므로 해당 DIO의 HIGH 또는 LOW값을 써주게 됨 
  • 이때, for문이 모두 끝나고 나면 HIGH에서 끝나기에 기본적으로 데이터를 안보낼 때는 HIGH 상태임을 알 수 있음 

(2) 2번째 send 함수 

  • 평상시엔 LOW로 있다가 HIGH로 감
  • 상승 엣지에서 데이터를 내보내고 하강 엣지에선 데이터 hold

 

 

(3) 나머지 함수들 

기존 c++ 코드

 

최적화 코드

 

 

(4) 선언부

(왼) 기존 c++ 코드 (오) 최적화 코드( pinMode 함수와 아래 SCLK, RCLK, DIO 초기화 구문은 우리가 ioc에서 설정해주었기에 생략)

(5) true, false 정의

 

(6) 최종 코드

#include "fnd_controller.h"

#define HIGH 1
#define LOW 0

#define false 0
#define true 1

uint8_t _LED_0F[29];

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 -

}

void send(uint8_t X){
	for (int i = 8; i >= 1; i--)
	{
	  if (X & 0x80)
	  {
		  HAL_GPIO_WritePin(FND_DIO_GPIO_Port, FND_DIO_Pin, HIGH);
	  }
	  else
	  {
		  HAL_GPIO_WritePin(FND_DIO_GPIO_Port, FND_DIO_Pin, LOW);
	  }
	  X <<= 1;

	  HAL_GPIO_WritePin(FND_SCLK_GPIO_Port, FND_SCLK_Pin, LOW);

	  HAL_GPIO_WritePin(FND_SCLK_GPIO_Port, FND_SCLK_Pin, HIGH);
	}
}


void send_port(uint8_t X, uint8_t port)
{
  send(X);
  send(port);
  HAL_GPIO_WritePin(FND_RCLK_GPIO_Port, FND_RCLK_Pin, HIGH);

  HAL_GPIO_WritePin(FND_RCLK_GPIO_Port, FND_RCLK_Pin, LOW);
}


void digit4_show(int n, int replay, uint8_t showZero)
{
  int n1, n2, n3, n4;
  n1 = (int)  n % 10;
  n2 = (int) ((n % 100)-n1)/10;
  n3 = (int) ((n % 1000) - n2 - n1) / 100;
  n4 = (int) ((n % 10000) - n3 - n2 - n1) / 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);
 }
}

void digit4_replay(int n, int replay)
{
  digit4_show(n,replay,false);
}

void digit4(int n)
{
  digit4_show(n,0,false);
}

void digit4showZero_replay(int n, int replay)
{
	digit4_show(n, replay, true);
}

void digit4showZero(int n)
{
	digit4_show(n, 0, true);
}


void digit2(int n, int port, int replay)
{
  int n1, n2;
  n1 = (int)  n % 10;
  n2 = (int) ((n % 100)-n1)/10;

   for(int i = 0; i<=replay; i++){
   send_port(_LED_0F[n1], port);
   send_port(_LED_0F[n2], port<<1);
   }
}

void digit2_port(int n, int port)
{
	digit2(n, port, 0);
}

 

(7) find_controller.c 의 모든 소스코드를 find_controller.h에 추가하여 함수의 헤더부분만 정의 

 

(8) 최종 소스코드와 헤더파일 

: 위의 사진들 중에서 오류가 있기에 아래 코드만 참조 

#include "fnd_controller.h"
uint8_t _LED_0F[29];


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 -

}

void send(uint8_t X){
	for (int i = 8; i >= 1; i--)
	{
	  if (X & 0x80)
	  {
		  HAL_GPIO_WritePin(FND_DIO_GPIO_Port, FND_DIO_Pin, HIGH);
	  }
	  else
	  {
		  HAL_GPIO_WritePin(FND_DIO_GPIO_Port, FND_DIO_Pin, LOW);
	  }
	  X <<= 1;

	  HAL_GPIO_WritePin(FND_SCLK_GPIO_Port, FND_SCLK_Pin, LOW);

	  HAL_GPIO_WritePin(FND_SCLK_GPIO_Port, FND_SCLK_Pin, HIGH);
	}
}


void send_port(uint8_t X, uint8_t port)
{
  send(X);
  send(port);
  HAL_GPIO_WritePin(FND_RCLK_GPIO_Port, FND_RCLK_Pin, HIGH);

  HAL_GPIO_WritePin(FND_RCLK_GPIO_Port, FND_RCLK_Pin, LOW);
}


void digit4_show(int n, int replay, uint8_t showZero)
{
  int n1, n2, n3, n4;
  n1 = (int)  n % 10;
  n2 = (int) ((n % 100)-n1)/10;
  n3 = (int) ((n % 1000) - n2 - n1) / 100;
  n4 = (int) ((n % 10000) - n3 - n2 - n1) / 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);
 }
}

void digit4_replay(int n, int replay)
{
  digit4_show(n,replay,false);
}

void digit4(int n)
{
  digit4_show(n,0,false);
}

void digit4showZero_replay(int n, int replay)
{
	digit4_show(n, replay, true);
}

void digit4showZero(int n)
{
	digit4_show(n, 0, true);
}


void digit2(int n, int port, int replay)
{
  int n1, n2;
  n1 = (int)  n % 10;
  n2 = (int) ((n % 100)-n1)/10;

   for(int i = 0; i<=replay; i++){
   send_port(_LED_0F[n1], port);
   send_port(_LED_0F[n2], port<<1);
   }
}

void digit2_port(int n, int port)
{
	digit2(n, port, 0);
}

 

/*
 * fnd_controller.h
 *
 *  Created on: Jan 24, 2025
 *      Author: mj492
 */


#ifndef SRC_FND_CONTROLLER_H_
#define SRC_FND_CONTROLLER_H_
#include "main.h"


#define HIGH 1
#define LOW 0

#define false 0
#define true 1



void init_fnd();

void send(uint8_t X);
void send_port(uint8_t X, uint8_t port);

void digit4_show(int n, int replay, uint8_t showZero);
void digit4_replay(int n, int replay);
void digit4(int n);

void digit4showZero_replay(int n, int replay);
void digit4showZero(int n);
void digit2(int n, int port, int replay);
void digit2_port(int n, int port);

#endif /* SRC_FND_CONTROLLER_H_ */

 


(9) main 함수에서 실제 실행해보기

  • main.c에 #include "fnd_controller.h"
  • main 함수에 init_fnd(); 함수 실행 
  • while문 내에 99까지 숫자 증가하는 코드 작성 

 

 

 


+ 다른 작업도 수행해보기

 

EX1. 9999까지 출력

 

 


 

EX2. A 출력해보기