UART 활용
2023. 3. 6. 17:38ㆍAVR ATmega128
UART를 더 쉽게 사용하기 위해 코드를 분리하였다.
/*
* UART.h
*
* Created: 2023-03-06 오후 2:14:16
* Author: yrt12
*/
#ifndef UART0_H_
#define UART0_H_
#include <avr/io.h>
void UART0_init();
void UART0_Transmit( unsigned char data );
unsigned char UART0_Receive();
void UART0_print(char *str);
uint8_t UART0_available();
void UART0_ISR_Process();
uint8_t UART0_readyRxFlag();
void UART0_clearRxFlag();
void UART0_setRxFlag();
uint8_t *UART0_readBuffer();
#endif /* UART_H_ */
/*
* UART0.c
*
* Created: 2023-03-06 오후 2:14:06
* Author: yrt12
*/
#include "UART0.h"
uint8_t uart0ReceiveBuff[100];
uint8_t uartRxCompleteFlag= 0;
uint8_t *UART0_readBuffer()
{
return uart0ReceiveBuff;
}
uint8_t UART0_readyRxFlag()
{
return uartRxCompleteFlag;
}
void UART0_clearRxFlag()
{
uartRxCompleteFlag = 0;
}
void UART0_setRxFlag()
{
uartRxCompleteFlag = 1;
}
void UART0_init()
{
//rx,tx사용, 2배모드 사용, 9600 Baudrate
//8bit data, 패리티 사용안함, 1비트 Stop bit (기본값)
UCSR0B |= 1<<RXEN0 | 1<<TXEN0 | 1<<RXCIE0;
UCSR0A |= 1<<U2X0;
UBRR0L = 207; //16 : 115200, 207 : 9600
}
void UART0_Transmit( unsigned char data )
{
/* Wait for empty transmit buffer */
while ( !( UCSR0A & (1<<UDRE0)) );
/* Put data into buffer, sends the data */
UDR0 = data;
}
unsigned char UART0_Receive()
{
/* Wait for data to be received */
while ( !(UCSR0A & (1<<RXC0)) );
/* Get and return received data from buffer */
return UDR0;
}
void UART0_print(char *str)
{
for (int i = 0; str[i]; i++)
{
UART0_Transmit(str[i]);
}
}
uint8_t UART0_available()
{
if(!(UCSR0A & (1<<RXC0)))
return 0; //
return 1;
}
void UART0_ISR_Process()
{
uint8_t rx0Data = UDR0;
static uint8_t uartRx0Tail = 0; //버퍼의 인덱스를 부를 때 tail이라는 말을 많이 사용
if(rx0Data == '\n') //문장이 끝났음을 알려주는 문자는 보통 \n사용
{
uart0ReceiveBuff[uartRx0Tail] = rx0Data;
uartRx0Tail++;
uart0ReceiveBuff[uartRx0Tail] = 0; //문자열의 마지막에는 \0이 나오기 때문에 문자열로 만들어주기 위해 0을 넣어줌 \0 = 0;
uartRx0Tail = 0;//문자열이 끝나면 버퍼위치 초기화
UART0_setRxFlag();
}
else
{
uart0ReceiveBuff[uartRx0Tail] = rx0Data;
uartRx0Tail++;
}
}
코드를 분리할 때, 원래 글로벌 변수를 사용하기 위해 함수들을 새로 만들어 주었다.
uint8_t UART0_readyRxFlag()
{
return uartRxCompleteFlag;
}
void UART0_clearRxFlag()
{
uartRxCompleteFlag = 0;
}
void UART0_setRxFlag()
{
uartRxCompleteFlag = 1;
}
컴플리트 플래그를 메인함수에서 사용하기 위한 함수이다.
uint8_t *UART0_readBuffer()
{
return uart0ReceiveBuff;
}
버퍼의 주소를 리턴해주는 함수이다. 배열에 액세스 하기 위해 배열의 주소값이 필요하기 때문에 uint_8t 형식의 포인터를 사용하여 버퍼의 첫 번째 주소를 리턴해준다.
/*
* USART.c
*
* Created: 2023-03-06 오전 9:27:20
* Author : yrt12
*/
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "periph/UART/UART0.h"
ISR(USART0_RX_vect)
{
UART0_ISR_Process();
}
int main(void)
{
/* Replace with your application code */
UART0_init();
SREG |= 0x80;
while (1)
{
if(UART0_readyRxFlag()){
UART0_clearRxFlag();
UART0_print("Received Data : ");
UART0_print((char *)UART0_readBuffer());
}
_delay_ms(1000);
}
}
메인함수는 글로벌변수들을 모두 위의 함수로 바꾼 것 외에는 차이가 없다.
여전히 잘 작동되는 모습을 볼 수 있다.
UART로 LED 제어하기
이번에는 UART로 LED를 제어해보자.
UART에서 들어온 값은 사용자가 만든 버퍼에 문자열로 저장이 된다. 이 문자열을 비교해서 같으면 실행되게 하려면 string.h을 추가해준다.
/*
* USART.c
*
* Created: 2023-03-06 오전 9:27:20
* Author : yrt12
*/
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <string.h>
#include "periph/UART/UART0.h"
#define LED_DDR DDRC
#define LED_PORT PORTC
#define LED1 PORTC0
#define LED2 PORTC1
#define LED3 PORTC2
ISR(USART0_RX_vect)
{
UART0_ISR_Process();
}
int main(void)
{
/* Replace with your application code */
UART0_init();
LED_DDR |= 1<<LED1 | 1<<LED2 | 1<<LED3;
UART0_print("LED Machine\n");
SREG |= 0x80;
while (1)
{
if(UART0_readyRxFlag()){
UART0_clearRxFlag();
UART0_print("Received Data : ");
UART0_print((char *)UART0_readBuffer());
uint8_t *rxString = UART0_readBuffer();
if(!strcmp((char *)rxString, "LED1_TOGGLE\n"))
{
LED_PORT ^= 1<<LED1;
}
else if(!strcmp((char *)rxString, "LED2_TOGGLE\n"))
{
LED_PORT ^= 1<<LED2;
}
else if(!strcmp((char *)rxString, "LED3_TOGGLE\n"))
{
LED_PORT ^= 1<<LED3;
}
else
{
LED_PORT = 0;
}
}
}
}
주의할점은 버퍼에 \n도 포함이 되어있기 때문에 비교할 문자열뒤에 \n을 넣어줘야한다.
사진
작동이 잘 되는 모습을 볼 수 있다.
2. UART에서 printf 사용하기
c언어에서 문자열을 프린트할때 보통 printf을 사용한다. AVR에서 printf을 추가하려면 코드를 추가해줘야한다.
원래 코드에
#include <stdio.h>
FILE OUTPUT = FDEV_SETUP_STREAM(UART0_Transmit,NULL,_FDEV_SETUP_WRITE);
int main()
{
stdin = &OUTPUT;
}
를 추가하면 printf를 사용할 수 있다.
/*
* USART.c
*
* Created: 2023-03-06 오전 9:27:20
* Author : yrt12
*/
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdio.h>
#include "periph/UART/UART0.h"
FILE OUTPUT = FDEV_SETUP_STREAM(UART0_Transmit,NULL,_FDEV_SETUP_WRITE);
FILE INPUT = FDEV_SETUP_STREAM(UART0_Receive,NULL,_FDEV_SETUP_READ);
#define LED_DDR DDRC
#define LED_PORT PORTC
#define LED1 PORTC0
#define LED2 PORTC1
#define LED3 PORTC2
ISR(USART0_RX_vect)
{
UART0_ISR_Process();
}
int main(void)
{
/* Replace with your application code */
UART0_init();
stdout = &OUTPUT;
stdin = &INPUT;
LED_DDR |= 1<<LED1 | 1<<LED2 | 1<<LED3;
UART0_print("LED Machine\n");
SREG |= 0x80;
while (1)
{
if(UART0_readyRxFlag()){
UART0_clearRxFlag();
UART0_print("Received Data : ");
uint8_t *rxString = UART0_readBuffer();
if(!strcmp((char *)rxString, "LED1_TOGGLE\n"))
{
LED_PORT ^= 1<<LED1;
if(LED_PORT & (1<<LED1))
printf("LED1 = ON\n");
else
printf("LED1 = Off\n");
}
else if(!strcmp((char *)rxString, "LED2_TOGGLE\n"))
{
LED_PORT ^= 1<<LED2;
if(LED_PORT & (1<<LED2))
printf("LED2 = ON\n");
else
printf("LED2 = Off\n");
}
else if(!strcmp((char *)rxString, "LED3_TOGGLE\n"))
{
LED_PORT ^= 1<<LED3;
if(LED_PORT & (1<<LED3))
printf("LED3 = ON\n");
else
printf("LED3 = Off\n");
}
}
}
}
이제 시리얼 모니터로 LEDn_TOGGLE을 입력하면 해당 LED가 켜지면서 시리얼 모니터로 해당 LED의 상태가 출력된다.
3. 시리얼 모니터로 선풍기 제어하기
/*
* Listener.c
*
* Created: 2023-02-27 오전 10:40:09
* Author: rhoblack
*/
#include "Listener.h"
button_t btnMode, btnTimer, btnTimerPlus;
void Listener_init()
{
Button_init(&btnTimer, &DDRA, &PINA, 1);
Button_init(&btnMode, &DDRA, &PINA, 0);
Button_init(&btnTimerPlus, &DDRA, &PINA, 2);
UART0_init();
}
void Listener_checkEvent() //선풍기의 상태 머신
{
uint8_t fanModeState = Model_getFanStateData();
uint8_t buzzerState = Model_getBuzzerOnStateData();
uint8_t *rxString = UART0_readBuffer();
switch(fanModeState)
{
case FAN_STOP :
/* printf("FANPOWER = STOP\n");*/
if((Button_getState(&btnMode) == ACT_RELEASED) || (!strcmp((char *)rxString, "FAN_POWER_ONE\n")))
{
buzzerState = BUZZER_POWERONSOUND;
fanModeState = FAN_POWER_ONE;
Model_setBuzzerOnStateData(buzzerState);
Model_setFanStateData(fanModeState);
}
else if((!strcmp((char *)rxString, "FAN_POWER_TWO\n")))
{
buzzerState = BUZZER_POWERONSOUND;
fanModeState = FAN_POWER_TWO;
Model_setFanStateData(fanModeState);
Model_setBuzzerOnStateData(buzzerState);
}
break;
case FAN_POWER_ONE:
// printf("FANPOWER = ONE\n");
Listener_timerEvent();
if((Button_getState(&btnMode) == ACT_RELEASED) || (!strcmp((char *)rxString, "FAN_POWER_TWO\n")))
{
buzzerState = BUZZER_BUTTONSOUND;
fanModeState = FAN_POWER_TWO;
Model_setFanStateData(fanModeState);
Model_setBuzzerOnStateData(buzzerState);
}
else if((!strcmp((char *)rxString, "FAN_POWER_OFF\n")))
{
buzzerState = BUZZER_POWEROFFSOUND;
fanModeState = FAN_STOP;
Model_setFanStateData(fanModeState);
Model_setBuzzerOnStateData(buzzerState);
}
break;
case FAN_POWER_TWO :
// printf("FANPOWER = TWO\n");
Listener_timerEvent();
if(Button_getState(&btnMode) == ACT_RELEASED)
{
buzzerState = BUZZER_POWEROFFSOUND;
fanModeState = FAN_STOP;
Model_setFanStateData(fanModeState);
Model_setBuzzerOnStateData(buzzerState);
}
else if((!strcmp((char *)rxString, "FAN_POWER_ONE\n")))
{
buzzerState = BUZZER_BUTTONSOUND;
fanModeState = FAN_POWER_ONE;
Model_setBuzzerOnStateData(buzzerState);
Model_setFanStateData(fanModeState);
}
else if((!strcmp((char *)rxString, "FAN_POWER_OFF\n")))
{
buzzerState = BUZZER_POWEROFFSOUND;
fanModeState = FAN_STOP;
Model_setBuzzerOnStateData(buzzerState);
Model_setFanStateData(fanModeState);
}
break;
}
*rxString = "";
}
void Listener_timerEvent()
{
uint8_t timerModeState = Model_getTimerStateData(); //오류의원인
uint8_t timerInitState = Model_getTimerInitData();
uint8_t buzzerState = Model_getBuzzerOnStateData();
switch(timerModeState)
{
case TIMER_OFF :
if(Button_getState(&btnTimer) == ACT_RELEASED)
{
buzzerState = BUZZER_BUTTONSOUND;
Model_setBuzzerOnStateData(buzzerState);
timerInitState = 1;
Model_setTimerInitData(timerInitState);
timerModeState = TIMER_30;
Model_setTimerStateData(timerModeState);
}
break;
case TIMER_30 :
if(Button_getState(&btnTimer) == ACT_RELEASED)
{
buzzerState = BUZZER_BUTTONSOUND;
Model_setBuzzerOnStateData(buzzerState);
timerInitState = 1;
Model_setTimerInitData(timerInitState);
timerModeState = TIMER_60;
Model_setTimerStateData(timerModeState);
}
break;
case TIMER_60 :
if(Button_getState(&btnTimer) == ACT_RELEASED)
{
buzzerState = BUZZER_BUTTONSOUND;
Model_setBuzzerOnStateData(buzzerState);
timerInitState = 0;
Model_setTimerInitData(timerInitState);
timerModeState = TIMER_OFF;
Model_setTimerStateData(timerModeState);
}
break;
}
}
해당 코드를 작성하면 시리얼 모니터와 버튼 모두로 선풍기를 조종할 수 있다.
'AVR ATmega128' 카테고리의 다른 글
GPIO (0) | 2023.03.07 |
---|---|
UART (0) | 2023.03.06 |
I2C(TWI) LCD (1) | 2023.03.03 |
초음파센서 드라이브 만들기 (0) | 2023.03.03 |
AVR초음파 센서 (0) | 2023.03.03 |