반응형

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 */
}
반응형
Posted by 덕력킹
,