UART 활용

2023. 3. 6. 17:38AVR 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;
		
			
		
	}
}

해당 코드를 작성하면 시리얼 모니터와 버튼 모두로 선풍기를 조종할 수 있다. 

 

Fan_Final.7z
0.09MB

'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