본문 바로가기
DSP, MCU/STM32 (ARM Cortex-M)

STM32 , 동기식 / 비동기식 상향/ 하향 카운터 프로그래밍으로 구현하기 ( JK 플립플롭 , IC 7490 / 7492 )

by eteo 2022. 5. 23.

 

 

4비트 상향 카운터

 

 

 

4비트 하향 카운터

 

 

 

 

 

 

 

 

 

main.h

먼저 main.h 에 플립플롭에 필요한 변수들을 묶어 구조체로 정의해주었다.

함수에 매개변수를 여러개 둘 필요없이 보다 간략하게 짤 수 있다.

 

/* USER CODE BEGIN Private defines */
typedef struct {
	uint8_t clockbefore;
	uint8_t Q;
	uint8_t Qbar;
}JKFF;
/* USER CODE END Private defines */

 

사실 하드웨어의 동작과 달리 여기서 Qbar의 역할은 없는데 그냥 넣었다.

 

 

 

 

 

main.c

뒤에 나오는 memset 함수 사용을 위해 string.h 를 포함해주었다.

 

/* USER CODE BEGIN Includes */
#include <string.h>
/* USER CODE END Includes */

 

 

 

함수의 원형들

/* USER CODE BEGIN PFP */

void JKflipflopF(uint8_t J, uint8_t K, uint8_t Pulse, JKFF *JKFFx);
void JKflipflopR(uint8_t J, uint8_t K, uint8_t Pulse, JKFF *JKFFx);
void A4upCounter(JKFF *jkff, uint8_t clock);
void A4downCounter(JKFF *jkff, uint8_t clock);
void S4upCounter(JKFF *jkff, uint8_t clock);
void S4downCounter(JKFF *jkff, uint8_t clock);
void ttl7490(JKFF *jkff, uint8_t clock);
void ttl7492(JKFF *jkff, uint8_t clock);

/* USER CODE END PFP */

 

 

 

 

4비트 카운터에 쓸거니까 요소가 4개인 구조체배열 생성

클락펄스로 사용할 변수 생성

 

  /* USER CODE BEGIN 2 */

  JKFF jkff[4]={0, };
  uint8_t clock = 0;

  /* USER CODE END 2 */

 

0으로 초기화 한다.

 

 

 

 

 

 

while문 내

 

  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  clock ^= 1;

	  ttl7490(jkff, clock); // can use ClockFlag also and delete HAL_Delay.

	  for(int i=0; i<4 ; i++){
		  if(jkff[i].Q)
			  GPIOD->ODR |= (0x80 >>i);
		  else
			  GPIOD->ODR &= ~(0x80 >>i);
	  }

	  HAL_Delay(500);

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

 

clock ^= 1; 이 0.5초 마다 클락펄스를 토글시키는 부분

저기 ttl7490 이라고 써있는 부분을 다른 함수로 바꾸면 다 사용 가능하다.

그리고 clock 변수 대신 timer 기능을 사용하여 클락펄스를 줄 수 도 있다.

 

for문은 카운터 함수 실행 후 output에 따라 led에 불을 켜고 끄는 부분이다.

 

 

 

 

 

 

 

JK 플립플롭 함수

void JKflipflopF(uint8_t J, uint8_t K, uint8_t Pulse, JKFF *JKFFx){

	if(Pulse == 0 && JKFFx->clockbefore == 1){
		if(J==1 && K==1){
			JKFFx->Q ^= 1;
		}else if(J==0 && K==1){
			JKFFx->Q = 0;
		}else if(J==1 && K==0){
			JKFFx->Q = 1;
		}
		JKFFx->Qbar = !JKFFx->Q;
	}
	JKFFx->clockbefore = Pulse;
}

void JKflipflopR(uint8_t J, uint8_t K, uint8_t Pulse, JKFF *JKFFx){

	if(Pulse == 1 && JKFFx->clockbefore == 0){
		if(J==1 && K==1){
			JKFFx->Q ^= 1;
		}else if(J==0 && K==1){
			JKFFx->Q = 0;
		}else if(J==1 && K==0){
			JKFFx->Q = 1;
		}
		JKFFx->Qbar = !JKFFx->Q;
	}
	JKFFx->clockbefore = Pulse;
}

 

JKflipflopF 는 Falling edge 방식이고 JKflipflopR 은 Rising edge 방식이다.

함수는 반환이 없는 void 형이고 매개변수는 J 와 K, 클락펄스 그리고 구조체 포인터이다. 주소로 받아서 값을 바로 수정하기 위해 그렇게 했다. 

 

함수 마지막에는 매개변수로 들어온 구조체 변수의 멤버인 clockbefore에 예전 펄스를 기억하게끔 했고 함수의 시작 부분에선 예전 펄스와 지금 매개변수로 들어온 펄스를 비교해서 Falling edge 또는 Rising edge 일 때만 동작하는 식이다.

 

 

 

 

 

비동기식 카운터. 상향/하향

void A4upCounter(JKFF *jkff, uint8_t clock){

	  JKflipflopF(1, 1, clock, &jkff[0]);
	  JKflipflopF(1, 1, jkff[0].Q, &jkff[1]);
	  JKflipflopF(1, 1, jkff[1].Q, &jkff[2]);
	  JKflipflopF(1, 1, jkff[2].Q, &jkff[3]);

}

void A4downCounter(JKFF *jkff, uint8_t clock){

	  JKflipflopR(1, 1, clock, &jkff[0]);
	  JKflipflopR(1, 1, jkff[0].Q, &jkff[1]);
	  JKflipflopR(1, 1, jkff[1].Q, &jkff[2]);
	  JKflipflopR(1, 1, jkff[2].Q, &jkff[3]);

}

 

아래 사진에서 보이듯 입력은 1, 1 로 고정인 T 플립플롭이고 두번째, 세번째, 네번째 플립플롭은 바로 전 플립플롭의 Q가 클락펄스로 들어온다

 

하향 카운터는 나머진 다 똑같고 Rising edge인 것만 다르다.

 

 

 

 

 

 

 

동기식 카운터. 상향/하향

void S4upCounter(JKFF *jkff, uint8_t clock){

	  uint8_t Jd = jkff[2].Q & jkff[1].Q & jkff[0].Q;
	  JKflipflopF(Jd, Jd, clock, &jkff[3]);

	  uint8_t Jc = jkff[1].Q & jkff[0].Q;
	  JKflipflopF(Jc, Jc, clock, &jkff[2]);

	  uint8_t Jb = jkff[0].Q;
	  JKflipflopF(Jb, Jb, clock, &jkff[1]);

	  JKflipflopF(1, 1, clock, &jkff[0]);

}


void S4downCounter(JKFF *jkff, uint8_t clock){

	  JKflipflopR(1, 1, clock, &jkff[0]);

	  uint8_t Jb = jkff[0].Q;
	  JKflipflopR(Jb, Jb, clock, &jkff[1]);

	  uint8_t Jc = jkff[1].Q & jkff[0].Q;
	  JKflipflopR(Jc, Jc, clock, &jkff[2]);

	  uint8_t Jd = jkff[2].Q & jkff[1].Q & jkff[0].Q;
	  JKflipflopR(Jd, Jd, clock, &jkff[3]);

}

 

비동기식과 다른점은 4비트의 플립플롭이 전부 같은 클락펄스를 입력으로 받는다. 실제 회로도와 똑같이 두번째, 세번째, 네번째 플립플롭의 입력인 Jb, Jc, Jd 를 변수로 선언하고 각각에 맞는 값을 대입해 주었다.

 

상향식과 하향식 둘 다 Falling edge 플립플롭을 썼다. 하향식 카운터는 처음 상태 0 이후에 그 다음 하강 엣지에 Q결과가 차례차례 다 1이되서 1111부터 시작하고, 상향식 카운터는 회로도와는 조금 다르지만 그냥 네번째 플립플롭부터 역순으로 배치해서 구현했다.

 

 

 

 

 

BCD 10진 카운터 7490, 6진 카운터 7492

void ttl7490(JKFF *jkff, uint8_t clock){

	JKflipflopF(1, 1, clock, &jkff[0]);
	JKflipflopF(1, 1, jkff[0].Q, &jkff[1]);
	JKflipflopF(1, 1, jkff[1].Q, &jkff[2]);
	JKflipflopF(1, 1, jkff[2].Q, &jkff[3]);

	if((!jkff[0].Q) && jkff[1].Q && !(jkff[2].Q) && jkff[3].Q){
		memset(&jkff[0], 0, sizeof(JKFF)*4);
	}

}

void ttl7492(JKFF *jkff, uint8_t clock){

	JKflipflopF(1, 1, clock, &jkff[0]);
	JKflipflopF(1, 1, jkff[0].Q, &jkff[1]);
	JKflipflopF(1, 1, jkff[1].Q, &jkff[2]);
	JKflipflopF(1, 1, jkff[2].Q, &jkff[3]);

	if(!(jkff[0].Q) && jkff[1].Q && jkff[2].Q && !(jkff[3].Q)){
		memset(&jkff[0], 0, sizeof(JKFF)*4);
	}

}

 

굉장히 간단하게 짰다. 10이 되는 순간(1010) / 6이 되는 순간(0110) 에 memset 함수로 다 0으로 초기화하면 처음부터 다시 카운트 된다.

 

 

 

IC 7490

10진 카운터

 

 

 

 

 

IC 7492

6진 카운터