__________

Designing the Future with Circuits

반도체 회로설계 취준기

하만(Harman) 세미콘 반도체 설계 과정/임베디드 시스템을 위한 SW 구조설계

하만(Harman) 세미콘 아카데미 60일차 - SW 구조설계(UART 시리얼 통신)

semicon_circuitdesigner 2024. 6. 7. 10:42

[2024.06.07.금] 인천인력개발원 하만 세미콘 아카데미


UART(Universal Asynchronous Reciever/Transmittter)


1. 시리얼 통신

  • 마이크로컨트롤러에서의 데이터 전송: 비트 단위의 데이터를 핀 단위로 전송
  • 데이터 전송 방법
    • 병렬 전송: 8개의 핀으로 한 번에 1바이트 데이터 전송
       - 연결이 복잡해짐
       - 핀 수가 제한적인 마이크로컨트롤러에서는 연결이 불가능할 수 있음
    • 직렬 전송: 1개의 핀으로 8번에 나누어 1바이트 데이터 전송
       - UART 통신은 시리얼/직렬 통신의 종류

2. 프로토콜

  • 컴퓨터(장치) 간의 데이터 통신에서 원활한 송수신을 위해 약속된 신호 송신의 순서, 속도, 데이터 표현법 등의 규약
  • UART 통신은 비동기(Asynchronous) 통신
    • 별도의 클록을 사용하지 않으므로 약속된 속도로 송수신 수행
    • 통신 단위로 보율(baud rate, =스위칭 속도) 사용

3. ATMega128에서의 UART 통신

  • 송신(RX, Receive Data)과 수신(TX, Transmit Data)의 2개 핀 사용
  • 하드웨어로 지원되는 2개의 UART 통신 포트 포함
     - UART0: PE0, PE1 핀
     - UART1: PD2, PD3 핀

4. UCSRnA 레지스터

  • U2Xn 비트: 2배속 설정
  • RXCn(Receive Complete) 비트: 수신 완료
  • UDREn(Data Register Empty) 비트: 송신 완료
  • UCSRnA 레지스터 비트
    • 7비트 - RXCn
      - 수신 버퍼(UDRn)에 읽지 않은 문자가 있을 때 1이 되고 버퍼가 비어있으면 0
      - UCSRnB 레지스터의 RXCIEn 비트와 함께 사용되어 수신 완료 인터럽트 발생 가능
    • 6비트 - TXCn(Transmit Complete)
      - 전송 시프트 레지스터에서 데이터를 송신하고 송신 버퍼(UDRn)도 비어있으면 1이 됨
      - UCSRnB 레지스터의 TXCIEn 비트와 함께 사용되어 송신 완료 인터럽트 발생 가능
    • 5비트 - UDREn(USART Data Register Empty)
      - 송신 버퍼(UDRn)가 비어 데이터를 받을 준비가 되면 1이 됨
      - UCSRnB 레지스터의 UDRIE0비트와 함께 사용되어 송신 데이터 레지스터 준비 완료 인터럽트 발생 가능
    • 4비트 - FEn(Frame Error)
      - 수신 데이터가 없는 경우 수신값은 HIGH 상태, 데이터 프레임 수신이 시작되는 시점에서 LOW 상태로 변경
      - 데이터 프레임 수신 종료 후 정비 비트를 수신할 때 수신값이 다시 HIGH 상태
      - 마지막에 프레임의 끝을 나타내는 신호를 수신하지 못하여 프레임 수신에 오류 발생 시 1의 값을 가짐
    • 3비트 - DDRn(Data Overrun Error)
      - 수신 버퍼가 가득 찬 상태에서 수신 시프트 레지스터에 새 문자가 수신되고, 그 다음 문자의 시작 비트가 검출되는 Overrun 상황 발생 시 1의 값
    • 2비트 - UPEn(USART Parity Error)
      - 수신 버퍼에 지정된 문자 데이터에 패리티 오류가 발생한 경우 1의 값
      - 패리티 비트 사용 설정된 경우에만 사용 가능
    • 1비트 - UZXn(Double the USART Transmission Speed)
      - 비동기 전송 모드에서만 사용
      - 2배속 모드면 1, 1배속 모드면 0의 값
    • 0비트 - MPCMn(Multi Processor Communication Mode)
      - 1개의 마스터 프로세스가 여러 개의 슬레이브 프로세서에게 특정 어드레스를 전송하여 1개의 슬레이브만을 지정, 데이터 전송하여 멀티프로세서 통신 모드 시 1의 값

5. UCSRnB 레지스터

  • 송수신 활성화 레지스터: 디폴트는 금지 상태
  • UCSRnB 레지스터 비트
    • 7비트 - RXCIEn(RX Complete Interrupt Enable): 수신 완료 인터럽트 발생 허용
    • 6비트 - TXCIEn(TX Complete Interrupt Enable): 송신 완료 인터럽트 발생 허용
    • 5비트 - UDRIEn(USART Data Register Empty Interrupt Enable): 송신 데이터 레지스터 준비 완료 인터럽트 발생 허용
    • 4비트 - RXENn(RX Enable): UART 수신기의 수신 기능 활성화
    • 3비트 - TXENn(TX Enable): UART 송신기의 송신 기능 활성화
    • 2비트 - UCSZn2(USART Character Size): UCSRnC 레지스터와 함게 전송 데이터의 비트 수 결정
    • 1비트 - RXB8n(Receive Data Bit 8): 데이터가 9비트인 경우 수신 데이터의 아홉 번째 비트(8번 비트) 저장을 위해 사용. 반드시 UDRn 레지스터보다 먼저 읽어야 함
    • 0비트 - TXB8n(Transmit Data Bit 8): 데이터가 9비트인 경우 송신 데이터의 아홉 번째 비트(8번 비트) 저장을 위해 사용. 반드시 UDRn 레지스터보다 먼저 써야 함  

6. UCSRnC 레지스터

  • 통신 설정 관련 레지스터 
  • UCSRnC 레지스터 비트
    • 6비트 - UMSELn: 0이면 비동기 USART(=UART), 1이면 동기 USART
    • 5비트, 4비트 - UPMn1, UPMn0 
    • 3비트 - USBSn: 0이면 1비트, 1이면 2비트
    • 2비트, 1비트 - UCSZn1, UCSZn0
    • 0비트 - UCPOLn

실습 1. UART 


1. Solution 우클릭 - Add - New Project 생성 - "test06-UART"

 

2. Board - ATMega128 선택

3. 위에서 생성한 프로젝트 우클릭 - Set As A StartUp Project

 

4. 코드 작성

더보기
/*
 * test06-UART.c
 *
 * Created: 2024-06-07 오전 10:36:22
 * Author : user
 */ 

#include <avr/io.h>
#include <avr/delay.h>

void initUART0(){//BaudRate 설정 9600N81
	UBRR0H = 0; //상위 UBRRn 비트는 0
	UBRR0L = 207;	//하위 UBRRn 비트를 207로 설정 -> Baud Rate : 9600
	
	UCSR0A |= (1 << 1);	//0000 0010b 2배속 설정
	
	UCSR0B |= (1<<RXEN0) | (1<<TXEN0); //RX, TX Enable
	UCSR0C |= 0x06;	//Data Bit : Default 8 (x11x)	0000 0100b N81
					//Stop Bit : 1 0xxx
	
}

void send(char c){//설정된 포트를 이용하여 1Char을 send
// 	while ( 1 ){
// 		if(UCSR0A & ( 1<<UDRE0 ) == 1) break;	//wait until data empty bit = 1
// 	};	
	while(!(UCSR0A & (1<<UDRE0)));	//wait until data empty bit = 1
	UDR0 = c;	//send 
}

int main(void)
{
    initUART0();	//Initialize
	char a = '0';
    while (1) {
		send(a++);	//보낼때마다 a를 증가시켜 0부터 증가하며 데이터 전송
		_delay_ms(1000);	//1000milli(=1) second delay
		if(a >= '9') a = '0';	//a가 0~9를 반복
    }
}

5. TeraTerm을 통해 UART 통신 확인

  • 프로그래밍 시 ISP/UART 스위치를 ISP로 설정 후 프로그래밍
  • 프로그래밍 완료 후 TeraTerm 확인 시 스위치를 UART로 바꿔 통신 실행

 

5. 문장을 송신하기 위한 코드 작성

더보기
/*
 * test06-UART.c
 *
 * Created: 2024-06-07 오전 10:36:22
 * Author : user
 */ 

#include <avr/io.h>
#include <avr/delay.h>

void initUART0(){//BaudRate 설정 9600N81
	UBRR0H = 0; //상위 UBRRn 비트는 0
	UBRR0L = 207;	//하위 UBRRn 비트를 207로 설정 -> Baud Rate : 9600
	
	UCSR0A |= (1 << 1);	//0000 0010b 2배속 설정
	
	UCSR0B |= (1<<RXEN0) | (1<<TXEN0); //RX, TX Enable
	UCSR0C |= 0x06;	//Data Bit : Default 8 (x11x)	0000 0100b N81
					//Stop Bit : 1 0xxx
	
}

void uPutc(char c){//설정된 포트를 이용하여 1Char을 send
// 	while ( 1 ){
// 		if(UCSR0A & ( 1<<UDRE0 ) == 1) break;	//wait until data empty bit = 1
// 	};	
	while(!(UCSR0A & (1<<UDRE0)));	//wait until data empty bit = 1
	UDR0 = c;	//send 
}

void uPuts(char *str){//설정된 포트를 이용하여 1string을 send
	while(*str) uPutc(*str++);	//str길이만큼 str의 한글자씩 출력\
	
	
// 	while(1){
// 		if(*str == 0) break;
// 		uPutc(*str);
// 		str++;
//	}
}

int main(void)
{
    initUART0();	//Initialize
	char a = '0';
	uPuts("안녕하세요\r\n");
    while (1) {
		uPutc(a++);	//보낼때마다 a를 증가시켜 0부터 증가하며 데이터 전송
		_delay_ms(1000);	//1000milli(=1) second delay
		if(a >= '9') a = '0';	//a가 0~9를 반복
    }
}

 

 

6. 문자열을 Buffer 공간에 넣어서 출력: buf공간 선언 및 main 함수 변경

더보기
}
unsigned char buf[1024];
void bPrint(){ //고정된 메모리 공간 buffer 내의 문자열 촐력
	uPuts(buf);
}

int main(void)
{
    initUART0();	//Initialize
	char a = '0';
	int i = 0;
	uPuts("안녕하세요\r\n");
    while (1) {
		//uPutc(a++);	//보낼때마다 a를 증가시켜 0부터 증가하며 데이터 전송
		sprintf(buf, "ATmega128 터미널 출력 테스트... #%d\r\n", i++);	bPrint();//buf공간 내의 값을 i를 증가시키며 하나식 출력
		_delay_ms(1000);	//1000milli(=1) second delay
		//if(a >= '9') a = '0';	//a가 0~9를 반복
    }
}

 

7. TeraTerm 확인

 


GitHub에 로컬 디렉토리 업로드


1. cmd 실행

2. git이 설치된 폴더로 이동(IncheonHarman2024)

3. git commit -m 1입력

4. git push 입력


실습 2. UART 이용한 가변저항 출력


1. 저번 시간 실습의 가변저항 출력 코드와 융합하여 코드 작성

더보기
/*
 * ADC.c
 *
 * Created: 2024-05-31 오후 4:33:58
 * Author : user
 */ 

#include <avr/io.h>
#include "myHeader.h"
#include <avr/delay.h>
#include <avr/interrupt.h>

int cint = 0; tcnt = 0;
void initUART0(){//BaudRate 설정 9600N81
	UBRR0H = 0; //상위 UBRRn 비트는 0
	UBRR0L = 207;	//하위 UBRRn 비트를 207로 설정 -> Baud Rate : 9600
	
	UCSR0A |= (1 << 1);	//0000 0010b 2배속 설정
	
	UCSR0B |= (1<<RXEN0) | (1<<TXEN0); //RX, TX Enable
	UCSR0C |= 0x06;	//Data Bit : Default 8 (x11x)	0000 0100b N81
	//Stop Bit : 1 0xxx
	
}

void uPutc(char c){//설정된 포트를 이용하여 1Char을 send
	// 	while ( 1 ){
	// 		if(UCSR0A & ( 1<<UDRE0 ) == 1) break;	//wait until data empty bit = 1
	// 	};
	while(!(UCSR0A & (1<<UDRE0)));	//wait until data empty bit = 1
	UDR0 = c;	//send
}

void uPuts(char *str){//설정된 포트를 이용하여 1string을 send
	while(*str) uPutc(*str++);	//str길이만큼 str의 한글자씩 출력\
	
	
	// 	while(1){
	// 		if(*str == 0) break;
	// 		uPutc(*str);
	// 		str++;
	//	}
}
unsigned char buf[1024];
void bPrint(){ //고정된 메모리 공간 buffer 내의 문자열 촐력
	uPuts(buf);
}

void initADC(unsigned char ch){
	ADMUX |= (1 << REFS0);	//AVCC를 기준 전압으로 선택하는 코드. REFSn이 기준 전압 설정. <<는 shift 의미
	
	ADCSRA |= 0x07;			//분주비 설정(하위 3비트)
	ADCSRA |= 1 << ADEN;	//ADC 활성화
	ADCSRA |= 1 << ADFR;	//Free-Running mode 설정
	
	ADMUX = (ADMUX & 0xE0) | ch;
	ADCSRA |= 1 << ADSC;
	
}

int main(void)
{
	initUART0();
    initADC(0);	//1: ch number
	int i = 0;
    while (1) 
    {
		while(!(ADCSRA & (1<<ADIF))); //ADCStateResistor이 읽을 준비가 되지 않았다면 기다려라
		int cnt = ADC;
		sprintf(buf, "가변저항 값: %d\r\n", cnt); bPrint();
		_delay_ms(1000);
    }
}

2. TeraTerm 출력 확인