2023.10.11 17일차
💡 unsigned란?
자료형에 unsigned가 붙게 되면 부호가 없다는 의미가 되는데, 이는 곧 음의 부호(-)를 붙이지 않고 0 이상의 값만을 표현하겠다는 의미이다.
정수 기반 자료형은 signed와 unsigned가 있다. 보통 사용할 데이터가 양수와 음수 값 모두라면 signed 자료형을 선택하고, 양수 값만 사용한다면 unsigned 자료형을 선택한다.
음수 처리를 하려면 부호 비트가 필요하기 때문에 양수의 저장 범위가 반으로 줄어들어 signed가 무조건 좋다고 이야기할 순 없다.
예를 들어 signed char 자료형은 데이터의 표현 범위가 -128 ~ 127까지 총 256개의 값을 저장할 수 있고, unsigned char 자료형은 데이터 표현 범위가 0 ~ 255까지 총 256개의 값을 저장할 수 있어 실제로 ‘저장 가능한 숫자(표현 가능한 숫자)’의 개수는 동일하다.
따라서 signed와 unsigned는 상황에 맞춰서 사용하는 것이다. 사용하는 값이 양수만 존재하는 경우에는 unsigned 자료형을 사용해 양수의 범위를 2배로 확대시킬 수 있다. 반면 사용하는 값에 음수가 존재한다면 signed를 선택할 수밖에 없다.
💡 UART란?
UART는 UART(Universal Asynchronous Receiver/Transmitter, 범용 비동기 송수신기)의 약자이다.
병렬 데이터의 형태를 직렬 방식으로 전환하여 데이터를 전송하는 컴퓨터 하드웨어의 일종이다.
UART를 이용한 통신 방식의 종류
UART에서 데이터는 프레임 형태로 전송된다.
(UART에 대한 내용은 새로운 포스트에 자세히 정리해야겠다..)
💡 volatile이란?
변수를 선언할 때 앞에 volatile을 붙이면 컴파일러는 해당 변수를 최적화에서 제외하여 항상 메모리에 접근하도록 한다. 즉, volatile 변수를 참조할 경우 레지스터에 로드된 값을 사용하지 않고 매번 메모리를 참조한다.
volatile [type] [variable_name];
필요성
반복문을 돌면서 1부터 10까지 더하는 프로그램이 있다고 가정하자.
이 경우 컴파일러는 최적화를 위해 반복문이 실행되지 않고 바로 1부터 10까지의 합을 출력하는데, 일반적인 코드라면 이런 최적화를 통해 수행 속도 면에서 이득을 보게 된다.
하지만 이 코드가 메모리 주소에 연결된 하드웨어 레지스터에 값을 쓰는 프로그램이라면 이야기가 달라진다. 각각의 쓰기가 하드웨어에 특정 명령을 전달하는 것이므로, 주소가 같다는 이유만으로 중복되는 쓰기 명령을 없애 버리면 하드웨어가 오동작하게 될 것이다. 이런 경우 유용하게 사용할 수 있는 키워드가 volatile이다. 변수를 volatile 타입으로 지정하면 앞서 설명한 최적화를 수행하지 않고 모든 메모리 쓰기를 지정한 대로 수행한다.
사용하는 곳
1. MIMO(Memory-mapped I/O)
2. 인터럽트 서비스 루틴(Interrupt Service Routine)의 사용
3. 멀티 쓰레드 환경
세 가지 모드 공통점은 현재 프로그램의 수행 흐름과 상관없이 외부 요인이 변수 값을 변경할 수 있다는 점이다. 인터럽트 서비스 루틴이나 멀티 쓰레드 프로그램의 경우 일반적으로 스택에 할당하는 지역 변수는 공유하지 않으므로, 서로 공유되는 전역 변수의 경우에만 필요에 따라 volatile을 사용하면 된다.
체배: 원래의 신호를 크게 하는 것
분주: 원래의 신호를 나눈다. 원래의 신호보다 작게 만드는 것 (divider, prescale)
STM32F429ZI가 168 MHz로 동작을 하고 있다. (100 MHz로 가정)
100 MHz를 1ms로 count한다 했을 때,
1) T(주기) = 1/f(주파수) = 1/100,000,000 = 0.00000001 sec = 10 nsec (nano second)
2) 10ns clock count를 몇 번 세어야 하는가?
3) 100,000번을 세어야 1ms가 나온다
결론 : CPU 부하(load)가 많이 걸린다
즉, 분주를 해야 부하가 적게 걸린다.
168 MHz -> 1 MHz로 분주 (dividor)
T(주기) = 1/f(주파수) = 1/1,000,000 Hz = 0.000001sec = 1 MSec
결론적으로 100 MHz상에서 1ms를 count 하기 위해서는 100,000번의 펄스를 count해야 하므로 그만큼 CPU 부하가 많이 걸린다.
1 MHz에서 1ms를 count 하기 위해서는 1000번만 count하므로 CPU 부하가 적게 걸림을 알 수 있다.
분주를 하는 이유는 CPU 부하를 적게 걸리게 함이 가장 큰 목적이다.
0도 카운트를 하기 때문에 반드시 -1을 붙여줘야 함. 0도 유효 숫자
enabled 시켜주기
// main.c
static void MX_TIM10_Init(void);
TIM_HandleTypeDef htim10;
...
/* Initialize all configured peripherals */
...
MX_TIM10_Init();
...
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim10); // IT = Interrupt ADD_JIMIN_1011
/* USER CODE END 2 */
// move from Dirver/STM32F4xx_HAL_Driver/stm32f4xx_hal_tim.c to here
// enter here when every timer INT occurs
volatile int TIM10_10ms_counter = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM10)
{
TIM10_10ms_counter++;
}
}
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart3, &rx_data, 1); // assing to RX INT
HAL_UART_Receive_IT(&huart6, &bt_rx_data, 1); //for BT assign to RX INT
HAL_TIM_Base_Start_IT(&htim10); // IT = Interrupt ADD_JIMIN_1011
TIM10_10ms_counter = 0;
// led_main();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if (TIM10_10ms_counter >= 5) // 50ms
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); // LED1
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7); // LED2
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_14); // LED3
}
}
LED 1, 2, 3이 번갈아 가면서 나오는 코드 (LED123_toggle)
while (1)
{
if (TIM10_10ms_counter % 5 == 0) // 50ms
{
// TIM10_10ms_counter = 0;
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); // LED1
if (TIM10_10ms_counter % 10 == 0)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7); // LED2
}
if (TIM10_10ms_counter % 15 == 0)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_14); // LED3
}
}
}
혹은..
while (1)
{
if (TIM10_10ms_counter >= 5) // 50ms
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); // LED1
if (TIM10_10ms_counter >= 10)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7); // LED2
if (TIM10_10ms_counter >= 15)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_14); // LED3
TIM10_10ms_counter = 0;
}
}
}
}
결선
VCC: 빨간색 (+)
GRN: 검정색 (-)
온습도 센서 연결
분주로 168 MHz를 1 MHz로 만들어 MS_delay에 사용할 것
//main.c
/* USER CODE BEGIN PV */
UART_HandleTypeDef huart3;
UART_HandleTypeDef huart6;
/* USER CODE END PV */
void delay_us(unsigned long us);
static void MX_TIM11_Init(void);
/* USER CODE BEGIN PFP */
extern void DHT11_main(void);
void delay_us(unsigned long us);
/* USER CODE END PFP */
int main(void)
{
/* Initialize all configured peripherals */
MX_TIM11_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart3, &rx_data, 1); // assing to RX INT
HAL_UART_Receive_IT(&huart6, &bt_rx_data, 1); //for BT assign to RX INT
HAL_TIM_Base_Start_IT(&htim10); // IT = Interrupt ADD_JIMIN_1011
HAL_TIM_Base_Start_IT(&htim11); // IT = Interrupt ADD_JIMIN_1011
TIM10_10ms_counter = 0;
// led_main();
DHT11_main();
/* USER CODE END 2 */
}
// DHT11.c
#include "main.h"
#include "DHT11.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// extern TIM_HandleTypeDef htim2;
extern void delay_us (unsigned long us);
extern volatile int TDHT11_timer_counter;
void DHT11_main(void);
void DHT11_main(void)
{
uint8_t i_RH, d_RH, i_Tmp, d_Tmp;
// HAL_TIM_Base_Start_IT(&htim2);
DHT11_Init();
while(1)
{
DHT11_trriger();
DHT11_DataLine_Input();
DHT11_dumi_read();
i_RH = DHT11_rx_Data();
d_RH = DHT11_rx_Data();
i_Tmp = DHT11_rx_Data();
d_Tmp = DHT11_rx_Data();
DHT11_DataLine_Output();
HAL_GPIO_WritePin(DHT11_PORT, DHT11_DATA_RIN, GPIO_PIN_SET);
printf("[Tmp]%d\n",(int)i_Tmp);
printf("[Wet]%d\n",(int)i_RH);
// FND_Update(i_Tmp*100 + i_RH);
HAL_Delay(1500);
}
}
void DHT11_processing(void)
{
uint8_t i_RH, d_RH, i_Tmp, d_Tmp;
DHT11_trriger();
DHT11_DataLine_Input();
DHT11_dumi_read();
i_RH = DHT11_rx_Data();
d_RH = DHT11_rx_Data();
i_Tmp = DHT11_rx_Data();
d_Tmp = DHT11_rx_Data();
DHT11_DataLine_Output();
HAL_GPIO_WritePin(DHT11_PORT, DHT11_DATA_RIN, GPIO_PIN_SET);
printf("[Tmp]%d\n",(int)i_Tmp);
printf("[Wet]%d\n",(int)i_RH);
}
void DHT11_Init(void)
{
HAL_GPIO_WritePin(DHT11_PORT, DHT11_DATA_RIN, GPIO_PIN_SET);
HAL_Delay(3000);
return;
}
void DHT11_trriger(void)
{
HAL_GPIO_WritePin(DHT11_PORT, DHT11_DATA_RIN, GPIO_PIN_RESET);
HAL_Delay(20);
HAL_GPIO_WritePin(DHT11_PORT, DHT11_DATA_RIN, GPIO_PIN_SET);
delay_us(7);
return;
}
void DHT11_DataLine_Input(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/*Configure GPIO pin : PH0 */
GPIO_InitStruct.Pin = DHT11_DATA_RIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; //Change Output to Input
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
return;
}
void DHT11_DataLine_Output(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/*Configure GPIO pin : PH0 */
GPIO_InitStruct.Pin = DHT11_DATA_RIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; //Change Input to Output
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
return;
}
uint8_t DHT11_rx_Data(void)
{
uint8_t rx_data = 0;
for(int i = 0; i < 8; i++)
{
//when Input Data == 0
while( 0 == HAL_GPIO_ReadPin(DHT11_PORT, DHT11_DATA_RIN) );
#if 1
delay_us(40);
#else // org
delay_us(16);
#endif
rx_data<<=1;
//when Input Data == 1
if(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_DATA_RIN))
{
rx_data |= 1;
}
while( 1 == HAL_GPIO_ReadPin(DHT11_PORT, DHT11_DATA_RIN) );
}
return rx_data;
}
void DHT11_dumi_read(void)
{
while( 1 == HAL_GPIO_ReadPin(DHT11_PORT, DHT11_DATA_RIN) );
while( 0 == HAL_GPIO_ReadPin(DHT11_PORT, DHT11_DATA_RIN) );
while( 1 == HAL_GPIO_ReadPin(DHT11_PORT, DHT11_DATA_RIN) );
return;
}
// DHT11.h
/*
* DHT11.h
*
* Created on: 2019. 9. 2.
* Author: kccistc
*/
#ifndef SRC_DHT11_H_
#define SRC_DHT11_H_
#define DHT11_PORT GPIOA
#define DHT11_DATA_RIN GPIO_PIN_0
void DHT11_Init(void);
void DHT11_trriger(void);
void DHT11_DataLine_Input(void);
void DHT11_DataLine_Output(void);
uint8_t DHT11_rx_Data(void);
void DHT11_dumi_read(void);
#endif /* SRC_DHT11_H_ */
습도 2AH = 42%
온도 1AH = 26도
멋져요 :)