2017. 5. 14. 10:43ㆍ개발/ARM
UART의 포맷
Start | Data 0 | Data 1 | Data 2 | Data 3 | Data 4 | Data 5 | Data 6 | Data 7 | Stop |
start bit, Data 0~7, stop bit 이 순서이다.
Baudrate는 데이터 전송 속도의 측정치, 보통 초당 비트 bits 수 이다.
회로도를 보면 망고보드는 RS-232C level Converter와 연결되어 있다.
망고보드에 달려 있는 SP3232C를 거쳐 UART1과 UART2가 rs232 신호로 나오는 것을 알 수 있다.
참고로 UART1 (RX) - PA10
UART1 (TX) - PA9
UART2 (RX) - PA3
UART2 (TX) - PA2 에 핀이 설정되어 있다.
만약 RS232를 거치지 않고 본래의 UART신호를 사용하고 싶다면 점프선으로 SP3232C 칩의 다리에 직접 꽂아 주면 변환 되기전 신호를 사용할 수 있다.
USART - S가 의미하는 것은 동기화이다. 동기 및 비동기 둘다 지원 한다는 뜻이다.
망고보드 시스템 프로그래밍 완전정복 1, P74를 살펴보면 UART1은 APB2에 UART2는 APB1에 연결되어 있는 것을 볼 수 있다.
(UART1을 사용하기 위해서는 APB2에 클록을 인가해주어야 사용가능, UART2경우에는 APB1에 클록 인가)
1 2 3 4 5 | #define USART1_BASE (APB2PERIPH_BASE+0x3800) #define USART1 ((USART_TypeDef*) USART1_BASE) #define GPIO_USART GPIOA #define GPIO_USART_Rx_Pin GPIO_Pin_10 #define GPIO_USART_Tx_Pin GPIO_Pin_9 | cs |
UART1 은 0x4001 3800 ~ 0x4001 3BFF
UART2 는 0x4000 4400~ 0x4000 47FF 이다.
#define PERIPH_BASE ((uint32_t)0x40000000) ------> APB Memory는 0x40000000 부터 설정 되어있다.
#define APB1PERIPH_BASE PERIPH_BASE ------> APB1은 0x40000000 부터 시작
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) ------> APB2는 0x40010000 부터 시작
USART1_BASE 는 APB2PERIPH_BASE+0x3800 로 해주어야 UART1의 주소를 참조하게 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | typedef struct { __IO uint16_t SR; uint16_t RESERVED0; __IO uint16_t DR; uint16_t RESERVED1; __IO uint16_t BRR; uint16_t RESERVED2; __IO uint16_t CR1; uint16_t RESERVED3; __IO uint16_t CR2; uint16_t RESERVED4; __IO uint16_t CR3; uint16_t RESERVED5; __IO uint16_t GTPR; uint16_t RESERVED6; } USART_TypeDef; | cs |
레지스터가 32비트이지만 USART레지스터의 전체를 살펴보면 상위 16비트는 모두 RESERVED되어있다. 그래서 16비트 씩 따로 다루고있다.
main 함수에서 해야 할 일은 3가지의 함수를 추가하고, UART clock부분을 활성화 시켜주는것이다.
1. UART clock부분 활성화
위의 표에서 비트14를 활성화 시켜주면 된다. st사의 헤더파일을 이용하거나 직접 레지스터에 기록해 주면 된다.
1 2 3 | RCC->APB2ENR |= RCC_APB2Periph_USART1; // APB2에 클럭인가 (*(volatile unsigned *)0x40021018) |= 0x4000; // 레지스터에 직접 OR연산 | cs |
2. GPIO_Configuration() 함수를 추가
1 2 3 4 5 6 7 8 | GPIO_InitStructure.GPIO_Pin = GPIO_USART_Tx_Pin; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIO_USART, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_USART_Rx_Pin; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIO_USART, &GPIO_InitStructure); | cs |
Tx는 출력해야하므로 스피드와 모드를 둘다 설정
Rx는 입력받아야하므로 입력 모드를 설정
2. USART1_Init() 함수를 추가
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | void USART1_Init(void) { USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No ; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; /* Configure the USARTx */ USART_Init(USART1, &USART_InitStructure); /* Enable the USART1 */ USART1->CR1 |= CR1_UE_Set; } | cs |
3. Serial.PutString() 함수 추가
1 2 3 4 5 6 7 8 | void Serial_PutString(uint8_t *s) { while (*s != '\0') { SerialPutChar(*s); s ++; } } | cs |
여기서 SerialPutChar함수를 보면 다음과 같다.
1 2 3 4 5 | void SerialPutChar(uint8_t c) { USART_SendData(USART1, c); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } | cs |
여기서 또 USART_SendData 함수를 보자면
1 2 3 4 5 | void USART_SendData(USART_TypeDef* USARTx, uint16_t Data) { /* Transmit Data */ USARTx->DR = (Data & (uint16_t)0x01FF); } | cs |
여기서 새로운 레지스터(Data register)가 등장한다.
이 레지스터는 9비트만을 유효한 데이터로 가지고있다. 그래서 정확한 값을 넣기위해 Data와 0x01FF를 AND연산 시키고 있는 것이다.
USART에서 하나의 DR레지스터를 통하여 송수신을 모두 처리 할 수 있다. (내부적으로는 나누어서 동시에 처리)
SerialPutChar함수에서 보듯이 USART_GetFlagStatus 함수를 살펴보자면 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG) { FlagStatus bitstatus = RESET; if ((USARTx->SR & USART_FLAG) != (uint16_t)RESET) { bitstatus = SET; } else { bitstatus = RESET; } return bitstatus; } | cs |
Tx와 Rx가 동시에 처리되고 있기 때문에 언제 데이터를 받고 언제 데이터를 쓸지 알려주어야한다.
이 부분에서는 Status Register를 통해서 구현한다.
여기서 7번 비트 TXE를 살펴보자면 TXE는 Transmit data register empty의 약자로 하드웨어에 의해서 자동으로 설정이 되는 비트이다.
TDR 레지스터의 내용이 shift 레지스터로 전달이 완료 될때 자동으로 1로 설정이 된다.
USART_DR 레지스터에 write를 하게 되면 자동으로 0으로 clear 된다.
즉 TXE값이 0 이라면 데이터는 shift 레지스터로 이동되어지지 않았다. 라는 뜻이고
1 이라면 데이터는 shift 레지스터로 이동되어졌다는 뜻이다.
다시 정리하면 어느 한 바이트를 UART로 전송하기 위해 USART_DR 레지스터에 값을 쓰게되면 TXE비트가 자동으로 0이 되고,
이것이 1로 변할 때까지 기다려야 한다.
다시 SerialPutChar 함수에서 아래 부분은 USART_SendData를 통해 한 문자를 전송하고 TXE비트가 1이 될때까지 기다리는 동작을 뜻한다.
출처 : 망고스토리2.0 ARM Cortex-M3 시스템 프로그래밍 완전정복 1
http://www.ntrexgo.com/archives/21271
https://sites.google.com/site/johnkneenmicrocontrollers/sci_rs232/fci_f107
'개발 > ARM' 카테고리의 다른 글
[망고보드 M32] RS-232와 UART (2) | 2017.05.14 |
---|