STM32/활용

STM32로 DS1302제어하기(2편)

원원 2023. 1. 7. 23:56

안녕하세요. 오늘은 DS1302에 이전에썻던 1편에 이어서 2편을 쓰겠습니다. 
1편에서는 MCU랑 DS1302랑 통신을 하기위한 과정을 적었고 2편에서는 실제로 해보겠습니다
첫 번째로 DS1302로부터 '초'단위만 읽어서 시리얼모니터로 값을 확인해보겠습니다.

사용하는 MCU는 NUCLEO-F103RB 입니다. 먼저 GPIO설정을 해줍니다. DS1302랑 통신하기위해서는 IO포트 3개가 필요합니다.

그 다음에 DS1302를 write/read하기위해서 데이터시트에 나와있는 파형에 맞게 코드를 작성해줍니다

 

먼저 write 부터 알아보겠습니다

초기상태
CE핀 : LOW
SCK핀 : LOW
IO핀 : 사용자설정

* Write
1. CE핀 -> HIGH세팅
2. I_O핀 = OUTPUT 설정
3. I_O핀 -> HIGH or LOW
4. SCLK핀 -> HIGH
5. SCLK핀 -> LOW
6. 3~5번 과정 7번 더 반복
7 I_O핀 = OUTPUT 설정
8. I_O핀 -> HIGH or LOW
9. SCLK핀 -> HIGH
10. SCLK핀 -> LOW
11. 8~10번 과정 7번 더 반복
12. CE핀 -> LOW세팅

 

위의 과정에서 2~6번 과정을 함수로 만들어보겠습니다

void commandWrite(GPIO_TypeDef *GPIO_SCLK, uint16_t GPIO_Pin_SCLK, GPIO_TypeDef *GPIO_I_O, uint16_t GPIO_Pin_I_O,uint8_t value)
{
  /* I_O PORT OUTPUT SETTING */
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin = GPIO_Pin_I_O;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIO_I_O, &GPIO_InitStruct);
  int i;

  /* WRITE SETTING */
  for(i=0;i<8;i++) //LSB
  {
     if(((value>>i) & 0x01) ? 1 : 0 )
       HAL_GPIO_WritePin(GPIO_I_O, GPIO_Pin_I_O, 1);
     else
       HAL_GPIO_WritePin(GPIO_I_O, GPIO_Pin_I_O, 0);
     HAL_GPIO_WritePin(GPIO_SCLK, GPIO_Pin_SCLK, 1);
     HAL_GPIO_WritePin(GPIO_SCLK, GPIO_Pin_SCLK, 0);
  }
}

매개변수들은 SCLK, RST, IO핀에대한것들이고 value는 write할 값입니다.

다른코드는 어려운건없고 if(((value>>i) & 0x01) ? 1 : 0)에대해 적어보겠습니다.
DS1302는 LSB타입으로 데이터를 보내야합니다. 아래의 표를 보면 second를 write하는 부분을 보면 0x80을 보내야합니다. 근데 파형을 보면 [R/W` A0 A1 A2 A3 A4 R/C' 1]이라고 적혀있습니다. 0x80을 LSB로 보내야 합니다. 매개변수에 0x80을 넣고 파형에서는 0000 0001이라고 나오게하기위해서 if(((value>>i) & 0x01) ? 1 : 0) 을 했습니다. 
i = 0 일때 value >> 0 이고 가장작은비트의 값을 검사해서 HIGH or LOW 설정합니다.
i = 1 일때 value >> 1 을 해서 그 다음 비트의 값을 검사해서 HIGH or LOW 설정합니다.
이런 방식으로 한다면 매개변수에 0x80을 넣어도 파형은 0000 0001이 나오게 됩니다.

 

아래의 코드를 넣어서 파형을 찍어보겠습니다. 

{
	HAL_GPIO_WritePin(CE_GPIO_Port, CE_Pin, 1);
	commandWrite(SCLK_GPIO_Port, SCLK_Pin, I_O_GPIO_Port, I_O_Pin, 0x80);
	HAL_GPIO_WritePin(CE_GPIO_Port, CE_Pin, 0);
}

 

이제 read를 해보겠습니다

초기상태
CE핀 : LOW
SCK핀 : LOW
IO핀 : 사용자설정

* Read
1. CE핀 -> HIGH세팅
2. I_O핀 = OUTPUT 설정
3. I_O핀 -> HIGH or LOW
4. SCLK핀 -> HIGH
5. SCLK핀 -> LOW
6. 3~5번 과정 7번 더 반복
7. I_O핀 = INPUT 설정
8. I_O핀 READ
9. SCLK핀 -> HIGH
10. SCLK핀 -> LOW
11. 8~10번 과정 7번 더 반복
12. CE핀 -> LOW세팅

위의 과정에서 7~11번 과정을 함수로 만들어보겠습니다

uint8_t commandRead(GPIO_TypeDef *GPIO_SCLK, uint16_t GPIO_Pin_SCLK, GPIO_TypeDef *GPIO_I_O, uint16_t GPIO_Pin_I_O)
{
  /* I_O PORT INPUT SETTING */
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  HAL_GPIO_WritePin(GPIO_I_O, GPIO_Pin_I_O, 0);
  GPIO_InitStruct.Pin = GPIO_Pin_I_O;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIO_I_O, &GPIO_InitStruct);
  uint8_t value=0;
  uint8_t currentBit = 0;
  int i;

  /* INPUT SETTING */
  for(i=0;i<8;i++)
  {
    currentBit = HAL_GPIO_ReadPin(GPIO_I_O, GPIO_Pin_I_O);
    value |= (currentBit<<i);
    HAL_GPIO_WritePin(GPIO_SCLK, GPIO_Pin_SCLK, 1);
    HAL_GPIO_WritePin(GPIO_SCLK, GPIO_Pin_SCLK, 0);
  }
  return value;
}

read를 하려면 DS1302로부터 읽어야 하므로 먼저 IO를 input으로 바꿔야합니다. 그 다음 읽을때도 LSB이므로 value |= (currentbit << i)를 해서 먼저 읽는 비트를 가장작은비트에 저장합니다.

아래의 코드의 파형을 보겠습니다. (k는 uint8_t형 변수입니다)

{
    HAL_GPIO_WritePin(CE_GPIO_Port, CE_Pin, 1);
    k = commandRead(SCLK_GPIO_Port, SCLK_Pin, I_O_GPIO_Port, I_O_Pin);
    HAL_GPIO_WritePin(CE_GPIO_Port, CE_Pin, 0);
}

사실 write없이 read만 하면 의미가 없습니다. 데이터시트에 나와있는 파형을 보면 먼저 write를 하고 write해서 나오는 파형을 읽습니다.

 

이제 read / write파형을 만들 수 있으니 DS1302로부터 '초'단위만 읽어서 시리얼모니터로 값을 확인하는 작업을 할 수있습니다.
먼저 CLOCK을 사용한다고 WRITE를 해줘야 합니다.

CLOCK HALT FLAG를 start하기 위해서 CH에 0을 write 해줘야 합니다. second는 설정하지않고 CLOCK HALT FLAG를 start하기위해서 0x80 write , 0x00 write해주면 됩니다.

void DS1302_HALT(GPIO_TypeDef *GPIO_SCLK, uint16_t GPIO_Pin_SCLK, GPIO_TypeDef *GPIO_I_O, uint16_t GPIO_Pin_I_O, GPIO_TypeDef *GPIO_CE, uint16_t GPIO_Pin_CE, uint8_t flag)
{
  HAL_GPIO_WritePin(GPIO_CE, GPIO_Pin_CE, 1);
  commandWrite(GPIO_SCLK, GPIO_Pin_SCLK, GPIO_I_O, GPIO_Pin_I_O, 0x80);
  commandWrite(GPIO_SCLK, GPIO_Pin_SCLK, GPIO_I_O, GPIO_Pin_I_O, 0x00 | flag<<7);
  HAL_GPIO_WritePin(GPIO_CE, GPIO_Pin_CE, 0);
}

HALT를 함수로 만들었습니다. 매개변수 flag가 1이면 CLOCK이 멈추고 0이면 CLOCK이 start됩니다.

그 다음 초를 읽기위해서 READ를 해주면 됩니다. second를 read하기 위해서 0x81 write, read 하면 됩니다.

 

초를 읽는 코드입니다. 1초에 한번씩 읽은값을 printf 해주고 있습니다. 근데 여기서 중요한게 printf의 표시형식이 %x(16진수)입니다. 10진수로 출력안하고 16진수로 출력했는데 그 이유는 읽고나면 읽은 seconds 데이터 포맷이 16진수이므로 16진수로 printf했습니다.

{
	HAL_GPIO_WritePin(CE_GPIO_Port, CE_Pin, 1);
	commandWrite(SCLK_GPIO_Port, SCLK_Pin, I_O_GPIO_Port, I_O_Pin, 0x81);
	k = commandRead(SCLK_GPIO_Port, SCLK_Pin, I_O_GPIO_Port, I_O_Pin);
	HAL_GPIO_WritePin(CE_GPIO_Port, CE_Pin, 0);
	printf ("second : %x \n", k);
	HAL_Delay(1000);
}

 

예시로 파형몇개만 보겠습니다.

아래의 파형은 0x81로 read를하고 데이터를 읽었는데 읽은 데이터가 0x64이므로  0x26입니다.

아래의 파형은 read한값이 0xE4이므로 0x27입니다. 

그래서 printf를 하면 second : 26  second : 27라고 나오게 됩니다.

최종 결과입니다. 숫자가 1씩 증가하는걸 볼 수 있습니다

 

소스코드 https://github.com/yhunterr/STM32F103RB_DS1302/tree/main/DS1302_FIRST

'STM32 > 활용' 카테고리의 다른 글

STM32로 DS1302제어하기(3편)  (0) 2023.01.24
STM32로 DS1302제어하기(1편)  (0) 2023.01.04