[STM32#22] freeRTOS를 적용해서 녹칸다의 STM32쉴드에 붙어있는 여러가지 부분에 응용해보기!(녹칸다 내맘대로 STM32)
프로그래밍/STM32 2025. 12. 24. 15:42
https://youtube.com/live/snS0bWdWQZw
[STM32#22] freeRTOS를 적용해서 녹칸다의 STM32쉴드에 붙어있는 여러가지 부분에 응용해보기!(녹칸다 내맘대로 STM32)
심심한녹칸다의 내맘대로 STM32시리즈이다!
STM32시리즈의 모든 자료는 구글 슬라이드에 작성하고 모두에게 공유되어있음!
https://docs.google.com/presentation/d/1myA5iYbjuKsLWLqtRLKAiRfwUwvqB1d1RGjiMIIgp3I/edit?slide=id.g3b2ee18b41b_9_216#slide=id.g3b2ee18b41b_9_216
아주 약간만 응용하고 넘어가버리기!
1.로터리엔코더와 부저를 조합해서 아래와 같이 2개의 task를 rtos로 작동시키시오!
task1:로터리엔코더를 돌리면 LED8개가 시계방향 혹은 반시계방향으로 회전한다!
task2:버튼5개를 이용해서 도레미파솔까지 부저로 재생한다!

/* USER CODE BEGIN 4 */
GPIO_TypeDef *mygroup[] = {GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOC,GPIOB};
uint16_t mypin[] = {GPIO_PIN_3,GPIO_PIN_13,GPIO_PIN_5,GPIO_PIN_14,GPIO_PIN_4,GPIO_PIN_15,GPIO_PIN_4,GPIO_PIN_7};
void set_led(int num){
//LED의 상태 업데이트 현재 켜져야하는 LED는 CNT번째꺼
for(int i = 0;i<8;i++){
if(i == num){
HAL_GPIO_WritePin(mygroup[i],mypin[i],GPIO_PIN_SET);
}else{
HAL_GPIO_WritePin(mygroup[i],mypin[i],GPIO_PIN_RESET);
}
}
}
static void Tone(uint32_t Frequency)
{
TIM3->ARR = (1000000UL / Frequency) - 1; //주파수
TIM3->CCR3 = (TIM3->ARR >> 1); //듀티비 50%
}
#define NOTE_C6 1047 //도
#define NOTE_D6 1175 //레
#define NOTE_E6 1319 //미
#define NOTE_F6 1397 //파
#define NOTE_G6 1568 //솔
#define NOTE_A6 1760 //라
#define NOTE_B6 1976 //시
#define NOTE_C7 2093 //도
/* USER CODE END 4 */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
uint8_t old_A = 0;
int cnt = 0;
//시작하자마자 일단 12시방향의 LED는 1개 켜고 출발한다!
HAL_GPIO_WritePin(mygroup[0],mypin[0],GPIO_PIN_SET);
/* Infinite loop */
for(;;)
{
//녹칸다가 풀업방식으로 연결해놓음
uint8_t sw = HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_7);
if(sw == 0){
HAL_UART_Transmit(&huart2, "SW\n", 3, 100);
HAL_Delay(200);
}
//PA11의 상승엣지가 발생했을때
uint8_t now_A = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11);
if(old_A == 0 && now_A == 1){
//PA12의 값을 측정해서 LOW면 시계, HIGH면 반시계
uint8_t now_B = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12);
if(now_B){
//반시계
cnt--;
if(cnt < 0) cnt = 7;
set_led(cnt);
}else{
//시계
cnt++;
if(cnt > 7) cnt = 0;
set_led(cnt);
}
}
old_A = now_A;
osDelay(1); //1밀리초 지연~
}
/* USER CODE END 5 */
}
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
/* Infinite loop */
for(;;)
{
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_C6);
}else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_D6);
}else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_E6);
}else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_F6);
}else if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5) == 0){
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 1);
Tone(NOTE_G6);
}else{
//소리를 끈다
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, 0);
}
}
/* USER CODE END StartTask02 */
}
2.로터리엔코더+TM1637와 가변저항을 조합해서 아래와 같이 2개의 task를 rtos로 작동시키시오!
task1:로터리엔코더를 시계방향으로 돌리면 1씩 증가하고 반시계방향으로 돌리면 1씩 감소하는 카운터값이 있는데, 그 값을 FND모듈인 TM1637에 출력하시오!
task2:PA0에 연결된 가변저항값을 ADC로 읽어들여서 USART2로 시리얼 출력하시오!

/* USER CODE BEGIN 0 */
//주고받는 데이터 (사용자 정의)
typedef struct {
uint16_t id;
int16_t value;
} Msg_t;
//메시지 큐(전역)
osMessageQueueId_t dataQueue;
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
//메시지큐(메인)
dataQueue = osMessageQueueNew(
8, // queue length
sizeof(Msg_t), // item size
NULL);
/* USER CODE END 2 */
void DWT_Delay_Init(void)
{
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
void delay_us(uint32_t us)
{
uint32_t cycles = (SystemCoreClock / 1000000) * us;
uint32_t start = DWT->CYCCNT;
while ((DWT->CYCCNT - start) < cycles);
}
// TM1637 7세그먼트 숫자 코드 매핑
const char segmentMap[] = {
0x3f, 0x06, 0x5b, 0x4f,
0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c,
0x39, 0x5e, 0x79, 0x71,
0x00
};
// GPIOB 클럭 활성화 매크로
#define PORTB_CLK_ENABLE() (RCC->APB2ENR |= RCC_APB2ENR_IOPBEN)
// PB8, PB9 핀 사용 (GPIOB)
// PB8 = CLK, PB9 = DIO
// CLK와 DIO 핀 제어 함수
static inline void _tm1637ClkHigh(void) { GPIOB->BSRR = (1 << 8); } // CLK HIGH
static inline void _tm1637ClkLow(void) { GPIOB->BRR = (1 << 8); } // CLK LOW
static inline void _tm1637DioHigh(void) { GPIOB->BSRR = (1 << 9); } // DIO HIGH
static inline void _tm1637DioLow(void) { GPIOB->BRR = (1 << 9); } // DIO LOW
// 마이크로초 단위 딜레이
void _tm1637DelayUsec(unsigned int i)
{
delay_us(i); // 사용자 정의 delay_us 함수 사용
}
// TM1637 초기화 함수 (GPIO 출력 설정)
void tm1637Init(void)
{
PORTB_CLK_ENABLE(); // GPIOB 클럭 활성화
// GPIOB CRH 초기화 (PB8, PB9 출력 2MHz Push-Pull)
GPIOB->CRH &= ~((0xF << 0) | (0xF << 4)); // 해당 비트 클리어
GPIOB->CRH |= (0x2 << 0); // PB8: MODE=10(출력 2MHz), CNF=00(Push-Pull)
GPIOB->CRH |= (0x2 << 4); // PB9: MODE=10(출력 2MHz), CNF=00(Push-Pull)
tm1637SetBrightness(8); // 밝기 최대
}
// TM1637 시작 신호
void _tm1637Start(void)
{
_tm1637ClkHigh();
_tm1637DioHigh();
_tm1637DelayUsec(20);
_tm1637DioLow(); // START 조건: DIO LOW
}
// TM1637 종료 신호
void _tm1637Stop(void)
{
_tm1637ClkLow();
_tm1637DelayUsec(20);
_tm1637DioLow();
_tm1637DelayUsec(20);
_tm1637ClkHigh();
_tm1637DelayUsec(20);
_tm1637DioHigh(); // STOP 조건: DIO HIGH
}
// TM1637 ACK 무시 (결과 확인용)
void _tm1637ReadResult(void)
{
_tm1637ClkLow();
_tm1637DelayUsec(50);
// DIO 상태 확인, ACK 처리 (여기서는 실제 확인 없이 무시)
_tm1637ClkHigh();
_tm1637DelayUsec(20);
_tm1637ClkLow();
}
// TM1637 1바이트 전송
void _tm1637WriteByte(unsigned char b)
{
for (int i = 0; i < 8; ++i) {
_tm1637ClkLow();
if (b & 0x01) {
_tm1637DioHigh();
} else {
_tm1637DioLow();
}
_tm1637DelayUsec(30);
b >>= 1; // 다음 비트로 이동
_tm1637ClkHigh();
_tm1637DelayUsec(30);
}
}
// 밝기 설정 (0~8, 0 = 꺼짐)
void tm1637SetBrightness(char brightness)
{
_tm1637Start();
_tm1637WriteByte(0x87 + brightness); // 밝기 명령어
_tm1637ReadResult();
_tm1637Stop();
}
// 4자리 10진수 표시 (displaySeparator=1이면 3번째 자리 점 켬)
void tm1637DisplayDecimal(int v, int displaySeparator)
{
unsigned char digitArr[4];
int isNegative = 0;
if (v < 0) {
isNegative = 1;
v = -v;
}
// 숫자를 segmentMap으로 변환
for (int i = 0; i < 4; ++i) {
digitArr[i] = segmentMap[v % 10];
if (i == 2 && displaySeparator) {
digitArr[i] |= 1 << 7; // 점(DP) 켬
}
v /= 10;
}
// 음수 표시
if (isNegative) {
digitArr[3] = 0x40; // '-' 표시
}
// TM1637에 데이터 전송
_tm1637Start();
_tm1637WriteByte(0x40); // 자동 주소 증가 모드
_tm1637ReadResult();
_tm1637Stop();
_tm1637Start();
_tm1637WriteByte(0xc0); // 시작 주소 0
_tm1637ReadResult();
for (int i = 0; i < 4; ++i) {
_tm1637WriteByte(digitArr[3 - i]); // 3->0 순서로 전송
_tm1637ReadResult();
}
_tm1637Stop();
}
// 4자리 10진수 표시 (앞자리 0 제거, displaySeparator=1이면 점 켬)
void tm1637DisplayDecimalTrim(int v, int displaySeparator)
{
unsigned char digitArr[4];
int isNegative = 0;
if (v < 0) {
isNegative = 1;
v = -v;
}
// 숫자를 segmentMap으로 변환
for (int i = 0; i < 4; ++i) {
digitArr[i] = segmentMap[v % 10];
if (i == 2 && displaySeparator) {
digitArr[i] |= 1 << 7; // 점(DP) 켬
}
v /= 10;
}
// 앞자리 0 제거 (공백 처리)
int leadingZero = 1;
for (int i = 3; i >= 0; --i) {
if (digitArr[i] != segmentMap[0] || i == 0) {
leadingZero = 0;
}
if (leadingZero && digitArr[i] == segmentMap[0]) {
digitArr[i] = 0x00; // 공백
}
}
// 음수 표시
if (isNegative) {
digitArr[3] = 0x40; // '-'
}
// TM1637에 데이터 전송
_tm1637Start();
_tm1637WriteByte(0x40); // 자동 주소 증가 모드
_tm1637ReadResult();
_tm1637Stop();
_tm1637Start();
_tm1637WriteByte(0xC0); // 시작 주소 0
_tm1637ReadResult();
for (int i = 0; i < 4; ++i) {
_tm1637WriteByte(digitArr[3 - i]); // 3->0 순서로 전송
_tm1637ReadResult();
}
_tm1637Stop();
}
void tm1637SetRawSegment(uint8_t position, uint8_t segData)
{
// 주소 범위 보호
if (position > 3) return;
_tm1637Start();
_tm1637WriteByte(0x40); // 데이터 입력 모드
_tm1637ReadResult();
_tm1637Stop();
_tm1637Start();
_tm1637WriteByte(0xC0 + position); // 자리 선택
_tm1637ReadResult();
_tm1637WriteByte(segData); // 세그먼트 데이터 전송
_tm1637ReadResult();
_tm1637Stop();
}
/* USER CODE END 4 */
3.예제2에서 task1의 기능을 다음과 같이 수정하시오!(앗 usart2에 출력하는걸로 충돌발생)
task1:로터리엔코더를 시계방향으로 돌리면 1씩 증가하고 반시계방향으로 돌리면 1씩 감소하는 카운터값이 있는데, 그 값을 task3에 메시지큐로 전송한다!
task2:PA0에 연결된 가변저항값을 ADC로 읽어들여서 task3에 메시지큐로 전송한다!
task3:task1과 task2에서 받은 메시지를 usart2로 출력한다!

/* USER CODE BEGIN 0 */
//주고받는 데이터 (사용자 정의)
typedef struct {
uint16_t id;
int16_t value;
} Msg_t;
//메시지 큐(전역)
osMessageQueueId_t dataQueue;
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
//메시지큐(메인)
dataQueue = osMessageQueueNew(
8, // queue length
sizeof(Msg_t), // item size
NULL);
/* USER CODE END 2 */
void DWT_Delay_Init(void)
{
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
void delay_us(uint32_t us)
{
uint32_t cycles = (SystemCoreClock / 1000000) * us;
uint32_t start = DWT->CYCCNT;
while ((DWT->CYCCNT - start) < cycles);
}
// TM1637 7세그먼트 숫자 코드 매핑
const char segmentMap[] = {
0x3f, 0x06, 0x5b, 0x4f,
0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c,
0x39, 0x5e, 0x79, 0x71,
0x00
};
// GPIOB 클럭 활성화 매크로
#define PORTB_CLK_ENABLE() (RCC->APB2ENR |= RCC_APB2ENR_IOPBEN)
// PB8, PB9 핀 사용 (GPIOB)
// PB8 = CLK, PB9 = DIO
// CLK와 DIO 핀 제어 함수
static inline void _tm1637ClkHigh(void) { GPIOB->BSRR = (1 << 8); } // CLK HIGH
static inline void _tm1637ClkLow(void) { GPIOB->BRR = (1 << 8); } // CLK LOW
static inline void _tm1637DioHigh(void) { GPIOB->BSRR = (1 << 9); } // DIO HIGH
static inline void _tm1637DioLow(void) { GPIOB->BRR = (1 << 9); } // DIO LOW
// 마이크로초 단위 딜레이
void _tm1637DelayUsec(unsigned int i)
{
delay_us(i); // 사용자 정의 delay_us 함수 사용
}
// TM1637 초기화 함수 (GPIO 출력 설정)
void tm1637Init(void)
{
PORTB_CLK_ENABLE(); // GPIOB 클럭 활성화
// GPIOB CRH 초기화 (PB8, PB9 출력 2MHz Push-Pull)
GPIOB->CRH &= ~((0xF << 0) | (0xF << 4)); // 해당 비트 클리어
GPIOB->CRH |= (0x2 << 0); // PB8: MODE=10(출력 2MHz), CNF=00(Push-Pull)
GPIOB->CRH |= (0x2 << 4); // PB9: MODE=10(출력 2MHz), CNF=00(Push-Pull)
tm1637SetBrightness(8); // 밝기 최대
}
// TM1637 시작 신호
void _tm1637Start(void)
{
_tm1637ClkHigh();
_tm1637DioHigh();
_tm1637DelayUsec(20);
_tm1637DioLow(); // START 조건: DIO LOW
}
// TM1637 종료 신호
void _tm1637Stop(void)
{
_tm1637ClkLow();
_tm1637DelayUsec(20);
_tm1637DioLow();
_tm1637DelayUsec(20);
_tm1637ClkHigh();
_tm1637DelayUsec(20);
_tm1637DioHigh(); // STOP 조건: DIO HIGH
}
// TM1637 ACK 무시 (결과 확인용)
void _tm1637ReadResult(void)
{
_tm1637ClkLow();
_tm1637DelayUsec(50);
// DIO 상태 확인, ACK 처리 (여기서는 실제 확인 없이 무시)
_tm1637ClkHigh();
_tm1637DelayUsec(20);
_tm1637ClkLow();
}
// TM1637 1바이트 전송
void _tm1637WriteByte(unsigned char b)
{
for (int i = 0; i < 8; ++i) {
_tm1637ClkLow();
if (b & 0x01) {
_tm1637DioHigh();
} else {
_tm1637DioLow();
}
_tm1637DelayUsec(30);
b >>= 1; // 다음 비트로 이동
_tm1637ClkHigh();
_tm1637DelayUsec(30);
}
}
// 밝기 설정 (0~8, 0 = 꺼짐)
void tm1637SetBrightness(char brightness)
{
_tm1637Start();
_tm1637WriteByte(0x87 + brightness); // 밝기 명령어
_tm1637ReadResult();
_tm1637Stop();
}
// 4자리 10진수 표시 (displaySeparator=1이면 3번째 자리 점 켬)
void tm1637DisplayDecimal(int v, int displaySeparator)
{
unsigned char digitArr[4];
int isNegative = 0;
if (v < 0) {
isNegative = 1;
v = -v;
}
// 숫자를 segmentMap으로 변환
for (int i = 0; i < 4; ++i) {
digitArr[i] = segmentMap[v % 10];
if (i == 2 && displaySeparator) {
digitArr[i] |= 1 << 7; // 점(DP) 켬
}
v /= 10;
}
// 음수 표시
if (isNegative) {
digitArr[3] = 0x40; // '-' 표시
}
// TM1637에 데이터 전송
_tm1637Start();
_tm1637WriteByte(0x40); // 자동 주소 증가 모드
_tm1637ReadResult();
_tm1637Stop();
_tm1637Start();
_tm1637WriteByte(0xc0); // 시작 주소 0
_tm1637ReadResult();
for (int i = 0; i < 4; ++i) {
_tm1637WriteByte(digitArr[3 - i]); // 3->0 순서로 전송
_tm1637ReadResult();
}
_tm1637Stop();
}
// 4자리 10진수 표시 (앞자리 0 제거, displaySeparator=1이면 점 켬)
void tm1637DisplayDecimalTrim(int v, int displaySeparator)
{
unsigned char digitArr[4];
int isNegative = 0;
if (v < 0) {
isNegative = 1;
v = -v;
}
// 숫자를 segmentMap으로 변환
for (int i = 0; i < 4; ++i) {
digitArr[i] = segmentMap[v % 10];
if (i == 2 && displaySeparator) {
digitArr[i] |= 1 << 7; // 점(DP) 켬
}
v /= 10;
}
// 앞자리 0 제거 (공백 처리)
int leadingZero = 1;
for (int i = 3; i >= 0; --i) {
if (digitArr[i] != segmentMap[0] || i == 0) {
leadingZero = 0;
}
if (leadingZero && digitArr[i] == segmentMap[0]) {
digitArr[i] = 0x00; // 공백
}
}
// 음수 표시
if (isNegative) {
digitArr[3] = 0x40; // '-'
}
// TM1637에 데이터 전송
_tm1637Start();
_tm1637WriteByte(0x40); // 자동 주소 증가 모드
_tm1637ReadResult();
_tm1637Stop();
_tm1637Start();
_tm1637WriteByte(0xC0); // 시작 주소 0
_tm1637ReadResult();
for (int i = 0; i < 4; ++i) {
_tm1637WriteByte(digitArr[3 - i]); // 3->0 순서로 전송
_tm1637ReadResult();
}
_tm1637Stop();
}
void tm1637SetRawSegment(uint8_t position, uint8_t segData)
{
// 주소 범위 보호
if (position > 3) return;
_tm1637Start();
_tm1637WriteByte(0x40); // 데이터 입력 모드
_tm1637ReadResult();
_tm1637Stop();
_tm1637Start();
_tm1637WriteByte(0xC0 + position); // 자리 선택
_tm1637ReadResult();
_tm1637WriteByte(segData); // 세그먼트 데이터 전송
_tm1637ReadResult();
_tm1637Stop();
}
/* USER CODE END 4 */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
DWT_Delay_Init();
tm1637Init();
int cnt = 0;
//초기값 출력하기~
tm1637DisplayDecimalTrim(cnt,0);
uint8_t old_rot_A = 0;
Msg_t msg;
msg.id = 1;
/* Infinite loop */
for(;;)
{
if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_7) == 0){
cnt = 0;
tm1637DisplayDecimalTrim(cnt,0);
//cnt값을 메시지큐에 넣는다!
msg.value = cnt;
osMessageQueuePut(dataQueue, &msg, 0, 0); //송신
}
uint8_t now_rot_A = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11);
if(now_rot_A == 1 && old_rot_A == 0){
uint8_t now_rot_B = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12);
if(now_rot_B){
//CCW
cnt--;
}else{
//CW
cnt++;
}
tm1637DisplayDecimalTrim(cnt,0);
msg.value = cnt;
osMessageQueuePut(dataQueue, &msg, 0, 0); //송신
}
old_rot_A = now_rot_A;
osDelay(1);
}
/* USER CODE END 5 */
}
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
HAL_ADC_Start(&hadc1); //딱 한번만 호출
uint16_t adc_value0 = 0;
Msg_t msg;
msg.id = 2;
/* Infinite loop */
for(;;)
{
//측정할때마다 호출
if(HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK)
{
// 값 처리
adc_value0 = HAL_ADC_GetValue(&hadc1); //0~4095
msg.value = adc_value0;
osMessageQueuePut(dataQueue, &msg, 0, 0); //송신
}
osDelay(100);
}
/* USER CODE END StartTask02 */
}
void StartTask03(void *argument)
{
/* USER CODE BEGIN StartTask03 */
Msg_t msg;
char buff[50];
/* Infinite loop */
for(;;)
{
osMessageQueueGet(dataQueue, &msg, NULL, osWaitForever);
sprintf(buff,"id = %d, value= %d\n",msg.id,msg.value);
HAL_UART_Transmit(&huart2,buff,strlen(buff),100);
}
/* USER CODE END StartTask03 */
}
4.온습도센서와 가변저항과 1602LCD의 조합으로 아래와 같이 3개의 task를 구성하시오!
task1:0.1초간격으로 1씩 증가하는 카운터값을 task3에 메시지큐로 전송한다!
task2:PA0에 연결된 가변저항값을 ADC로 읽어들여서 task3에 메시지큐로 전송한다!
task3:task1과 task2에서 받은 메시지를 1602LCD에 출력하는데, task1의 값은 첫번째 line에 출력하고 가변저항값은 두번째 line에 출력하시오!

/* USER CODE BEGIN 0 */
//주고받는 데이터 (사용자 정의)
typedef struct {
uint16_t id;
int16_t value;
} Msg_t;
//메시지 큐(전역)
osMessageQueueId_t dataQueue;
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
//메시지큐(메인)
dataQueue = osMessageQueueNew(
8, // queue length
sizeof(Msg_t), // item size
NULL);
/* USER CODE END 2 */
/* USER CODE BEGIN 4 */
void DWT_Delay_Init(void)
{
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
void delay_us(uint32_t us)
{
uint32_t cycles = (SystemCoreClock / 1000000) * us;
uint32_t start = DWT->CYCCNT;
while ((DWT->CYCCNT - start) < cycles);
}
#define delay_ms osDelay
#define ADDRESS 0x27<<1
#define RS1_EN1 0x05
#define RS1_EN0 0x01
#define RS0_EN1 0x04
#define RS0_EN0 0x00
#define BackLight 0x08
// RS-Q0 / RW-Q1 / EN-Q2 / BackLight-Q3 / D4-Q4 / D5-Q5 / D6-Q6 / D7-Q7
void LCD_DATA(uint8_t data) {
uint8_t temp=(data & 0xF0)|RS1_EN1|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
temp=(data & 0xF0)|RS1_EN0|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
delay_us(4);
temp=((data << 4) & 0xF0)|RS1_EN1|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
temp = ((data << 4) & 0xF0)|RS1_EN0|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
delay_us(50);
}
void LCD_CMD(uint8_t cmd) {
uint8_t temp=(cmd & 0xF0)|RS0_EN1|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
temp=(cmd & 0xF0)|RS0_EN0|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
delay_us(4);
temp=((cmd << 4) & 0xF0)|RS0_EN1|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
temp=((cmd << 4) & 0xF0)|RS0_EN0|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
delay_us(50);
}
void LCD_CMD_4bit(uint8_t cmd) {
uint8_t temp=((cmd << 4) & 0xF0)|RS0_EN1|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
temp=((cmd << 4) & 0xF0)|RS0_EN0|BackLight;
while(HAL_I2C_Master_Transmit(&hi2c1, ADDRESS, &temp, 1, 1000)!=HAL_OK);
delay_us(50);
}
void LCD_INIT(void) {
delay_ms(100);
LCD_CMD_4bit(0x03); delay_ms(5);
LCD_CMD_4bit(0x03); delay_us(100);
LCD_CMD_4bit(0x03); delay_us(100);
LCD_CMD_4bit(0x02); delay_us(100);
LCD_CMD(0x28); // 4 bits, 2 line, 5x8 font
LCD_CMD(0x08); // display off, cursor off, blink off
LCD_CMD(0x01); // clear display
delay_ms(3);
LCD_CMD(0x06); // cursor movint direction
LCD_CMD(0x0C); // display on, cursor off, blink off
}
void LCD_XY(char x, char y) {
if (y == 0) LCD_CMD(0x80 + x);
else if (y == 1) LCD_CMD(0xC0 + x);
else if (y == 2) LCD_CMD(0x94 + x);
else if (y == 3) LCD_CMD(0xD4 + x);
}
void LCD_CLEAR(void) {
LCD_CMD(0x01);
delay_ms(2);
}
void LCD_PUTS(char *str) {
while (*str) LCD_DATA(*str++);
}
/* USER CODE END 4 */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
uint16_t cnt = 0;
Msg_t msg;
msg.id = 1;
/* Infinite loop */
for(;;)
{
cnt++;
msg.value = cnt;
osMessageQueuePut(dataQueue, &msg, 0, 0); //송신
osDelay(1000);
}
}
void StartTask02(void *argument)
{
HAL_ADC_Start(&hadc1); //딱 한번만 호출
uint16_t adc_value0 = 0;
Msg_t msg;
msg.id = 2;
/* Infinite loop */
for(;;)
{
//측정할때마다 호출
if(HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK)
{
// 값 처리
adc_value0 = HAL_ADC_GetValue(&hadc1); //0~4095
msg.value = adc_value0;
osMessageQueuePut(dataQueue, &msg, 0, 0); //송신
}
osDelay(100);
}
}
void StartTask03(void *argument)
{
/* USER CODE BEGIN StartTask03 */
Msg_t msg;
char buff[50];
DWT_Delay_Init(); //한번 호출하기~
LCD_INIT(); //1회호출(메인루프 시작전)
/* Infinite loop */
for(;;)
{
osMessageQueueGet(dataQueue, &msg, NULL, osWaitForever);
if(msg.id == 1){
sprintf(buff,"cnt = %10d",msg.value);
LCD_XY(0, 0) ; LCD_PUTS(buff);
}else if(msg.id == 2){
sprintf(buff,"PA0 value=%6d",msg.value);
LCD_XY(0, 1) ; LCD_PUTS(buff);
}
}
/* USER CODE END StartTask03 */
}

