2016년 2학기
컴퓨터하드웨어 실험 보고서
7조 6주차 실험
한성희, 이호욱, 한경수
개요
비동기(UART) 통신에 대해서 알아보는 실험이다. 다른것으로 USART 가 있는데 이것은 동기화 통신이라고 UART, 즉 비동기까지 지원하는 통신방법이다. 이번 실험에서는 UART 를 이용하여 테스트를 해봤다. UART 통신 동작의 이해는 여러 응용장치를 만드는데, 매우 필요한 기술이다. 비동기 통신 방법이 플래그 체크에 의한 Polling, 인터럽트 방식 등등이 있는데 우리가 실험해본 방식은 Polling 방식이다.
이 과정에서 오실로스코프 파형 관찰을 통한 전자적 신호에 대한 디버깅도 배울 수 있다.
세팅
eclipse
이전의 실험에서 rvc 나 기타 db 는 이미 생성되었다고 가정하고, eclipse 에서 추가적으로 stm32 에 대한 library 를 include 에 넣어놓는다. scatter 를 이용한 방식이 아닌 첫 실험때 사용한 방식으로 flash load 를 시킨다.
UART 통신 케이블
UART 통신을 위해 보드와 PC 를 케이블로 연결한다. 통신 전 “PL2303_Prolific_DriverInstaller_v130” 을 설치한다.
오실로스코프
먼저 커플링 DC 를 선택한다. 그리고 프로브를 보드 확장 포트의 GND 에 연결하고, 나머지 하나의 연결 고리를 우리가 측정하고자 하는 port 에 접촉시킨다. GND 는 공유되어있기 때문에 확장포트의 아무곳이나 프로브로 집으면 되고, PortA8, PortA9, PorA10 중에 하나에 연결하여 원하는 전압을 측정하면 된다.
RS-232C
위 그림과 같은 RS-232C 포트는 양방향 통신이 가능하며 각 단말에 TX, RX 가 쌍으로 있다. 그래서 한쪽에서 보내는것도 가능하고, 받는것도 가능하다. (1:1 통신만 가능하다.) 컴퓨터와 기기와 RS-232C 를 연결하면 컴퓨터의 장치관리자에 보면 COM port 가 추가로 잡힌모습을 볼 수 있는데, 만약 com port 의 번호가 우리가 원하지 않는 번호로 잡혀있다면 com port 수정을 해줄 수 있다. 터미널프로그램이 높은 번호의 com port 는 인지 못하는 경향이 있으니 꼭 체크한다.
USART 통신에 맞게 port를 연결하고 모드를 설정한다. MCO, TX, RX 에 해당하는 PA8, PA9, PA10에 연결한다.
전체적인 기계 연결 모습
기본 개념
동기식 / 비동기식 통신
동기식 통신
동기식 통신은 그림과 같이 데이터와는 별도로 송/수신 측이 하나의 기준 클럭으로 동기신호를 맞춰 동작한다. 수신측에서 클럭에 의해 비트를 구별하기 때문에 데이터와 클럭을 위한 2회선을 필요로 한다.
미리 정해진 수 만큼의 문자열을 한 묶음(블록 단위) 으로 하여 동시에 전송할 수 있다. 그래서 동기식은 고속 통신이다.
비동기식 통신
비동기식 통신은 동기식과 달리 클럭과 상관 없이 데이터는 송/수신간 동기를 맞추지 않고 문자 단위로 전송된다. 문자는 데이터 비트 부분과 시작비트(0), 정지비트(1)로 이루어져 전송된다.
한번에 한 문자씩 송/수신할 수 있다. 그래서 비동기식은 저속 통신이다.
시리얼 통신
시리얼 통신
시리얼 통신이란, 데이터를 직렬 형태로 한 bit 씩 전송하는 통신을 말한다.
비동기식 시리얼 통신
비동기식 시리얼 통신이란, 데이터가 외부 클럭 신호 도움 없이 몇 가지 규칙에 기반해 동작하는 통신을 말한다.
-
Data bits : 전송되는 데이터
-
Synchronization bits : 데이터의 시작과 끝, start/stop bit가 이에 해당된다.
-
Parity bits : 단순한 error 보정 bit
-
Baud rate : 시리얼 라인으로 전송되는 데이터 속도(bps), 보통 115200을 상한선으로 사용한다.
UART 와 USART
UART
UART(Universal Asynchronous Receiver/Transmitter) 는 범용 비동기화 송수신기로
병렬 데이터를 직렬 형식으로 전환하여 데이터를 전송하는 컴퓨터 하드웨어의 일종이다.
시리얼 기반 통식 방식으로 보통 RS_232 를 통해 통신을 지원한다.
USART
USART(Universal Syn/Asynchronous Receiver/Transmitter) 는 범용 동기화 송수신기로
동기화 통신까지 지원하는 UART이다.
USART 흐름
그림에서 CR1, 2, 3 을 이용하여 통신 설정을 변경할 수 있다. Baud rate 를 설정하여 BRR 값을 구한다.
Baud rate 를 구하는 식은 다음과 같다.
USART 데이터 송/수신간
-
Start bits : 통신의 시작을 의미하는 것으로 0으로 설정된다.
-
Data Bits : 송/수신되는 데이터를 8-9 bit 로 나타낸다.
-
Parity bits : 오류 검증을 위한 값으로 레지스터 설정에 따라 짝/홀/사용안함 으로 선택된다.
-
Stop bits : 통신 종료를 의미하는 것으로 레지스터 설정에 따라 비트 수가 나뉜다.
Clock
STM32 MCU 의 clock 은 SYSCLK 으로 나타나 있는 system clock 을 기반으로 결정된다.
우리가 사용하는 Cortex-M3 보드에서는 2가지 clock 이 발생된다.
-
HSE clock : Cortex-M3 보드의 Oscillator 에서 방생되는 clock으로 25Mhz 의 값을 가진다.
-
HSI clock : 보드의 내부 클락으로 8Mhz 의 값을 가진다.
Clock Tree
Clock 의 이동 경로를 보여주는 Clock Tree 이다.
Tree를 보면, SYSCLK(System Clock) 은 HSI, HSE, PLL 의 출력 중 하나를 사용한다.
PLL은 HSI 와 HSE 를 곱하거나 나누어서 원하는 주파수 값을 만들 수 있다.
만약 48Mhz 가 필요하다면 기본으로 25Mhz 의 주파수가 HSE OSC 에서 나오게 되는데, 가능한 조합의 수중에 하나가
25Mhz / 5 / 5 * 12 * 4 = 48Mhz 이 된다.
PREDIV2 = /5, PLL2MUL = *12, PREDIV1 = /5, PLLMUL = *4
위 박스의 값을 통해 최종적으로 SYSCLK 이 48Mhz 이 된다.
1. System Clock
Clock Tree 중 SYSCLK 값은 Oscillator 에서 나오는 HSE clock 또는 내부 HSI clock 을 PLL로 변환하여 결정할 수 있다.
2. Clock 이용
위에서 구한 SYSCLK 값을 이용하여 FCLK, HCLK, PCLK 값을 구할 수 있다.
-
FCLK : CPU 에서 사용되는 Clock.
-
HCLK : AHB 버스에 사용되는 Clock 으로 memroy controller, interrupt controller 등에 사용된다.
-
PCLK : APB 버스에 사용되는 Clock 으로 GPIO, UART, SPI 등에 사용된다.
3. MCO
MCO(Microcontroller Clock Output) 이란, 보드 내부에서 사용되는 clock 을 외부로 출력하는 기능을 담당한다.
Oscilloscope 와 연결하여 clock 설정이 정상적인지 확인할 수 있고 어떤 내부 clock 을 외부로 출력할지 결정한다.
구현
register 초기화
RCC_CR 과 RCC_CFGR 을 이용하여 clock 관련 레지스터를 초기화 한다.
RCC_CR 로 HSI 를 enable, HSE 와 PLL 을 OFF 시키는 것으로 초기화.
RCC_CFGR 로 MCO 가 clock을 받지 않고 HSI 가 SYSCLK 가 되도록 초기화한다. 또, 각각의 레지스터가 곱하거나 나누는 동작을 하지 않도록 초기화한다.
초기화 시킨 코드
Clock 값 설정
이번 실험에서 주어진 Clock 값은 다음과 같다.
System Clock : 48MHz
HCLK : 24MHz
PCLK1 : 24MHz
PCKL2 : 12MHz
먼저 HSE, HSI 값을 가지고 SYSCLK(System Clock) 값을 48MHz 로 설정해야 한다.
이를 위해서는 HSE = 25MHz 값을 이용하여 아래와 같은 단계로 code 의 Configure PLLs 부분을 수정한다.
1. PREDIV2 에서 /5
2. PLL2MUL 에서 *12
3. PREDIV1 에서 /5
4. PLLMUL 에서 *48
5. SW multiplexer 에서 PLLCLK 값 선택
/* Configure PLLs ------------------------------------------------------*/
/* PLL configuration: PLLCLK = ???? */
// HSE = 25, PREDIV1_div2 : /2 , PLLMULL4: *4
//HSE, PLL, PULLMUL 사용을 위한 초기화
RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
//아래 코드에서 계산한 값에 *4
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_PLLMULL4);
/* PLL2 configuration: PLL2CLK = ???? */
/* PREDIV1 configuration: PREDIV1CLK = ???? */
//PREDIV2, PLL2MUL, PREDIV1 사용을 위한 초기화
RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |
RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
//HSE /5, *12, /5
RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL12 |
RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);
요구 조건에 따라 수정한 코드.
RCC_CFGR 명령어에서 MCO 관련 명령어를 통해 MCO 값으로 SYSCLK 가 선택되도록 설정하여 오실로스코프로 원하는 클럭 값 SYSCLK = 48MHz 가 나오는지 확인한다.
오실로스코프를 이용하여 SYSCLK = 48MHz 확인.
설정한 SYSCLK 값을 prescalor 를 이용하여 HCLK, PCLK1, PCLK2 값을 생성한다.
SYSCLK 값이 48MHz 임으로 먼저 AHB precalar 를 통해 /2 연산을 해준다.
그리고 PLCK2 를 위해 APB2 prescalar 에서 /2 를 추가로 해준다.
수정한 코드는 다음과 같다.
USART 사용
void UartInit(void) {
/*---------------------------- RCC Configuration -----------------------------*/
/* GPIO RCC Enable */
/* USART RCC Enable */
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
/* USART Pin Configuration */
GPIOA->CRH = (GPIO_CRH_MODE8 | GPIO_CRH_CNF8_1 | GPIO_CRH_MODE9 | GPIO_CRH_CNF9_1);
/*---------------------------- USART CR2 Configuration -----------------------*/
/* Clear STOP[13:12] bits */
/* Configure the USART Stop Bits, Clock, CPOL, CPHA and LastBit ------------*/
/* Set STOP[13:12] bits according to USART_StopBits value */
USART1->CR2 &= ~(USART_CR2_STOP | USART_CR2_CPOL | USART_CR2_CPHA);
/*---------------------------- USART CR1 Configuration -----------------------*/
/* Clear M, PCE, PS, TE and RE bits */
USART1->CR1 &= ~(USART_CR1_M | USART_CR1_PCE | USART_CR1_PS | USART_CR1_TE | USART_CR1_RE);
/* Configure the USART Word Length, Parity and mode ----------------------- */
/* Set the M bits according to USART_WordLength value */
/* Set PCE and PS bits according to USART_Parity value */
/* Set TE and RE bits according to USART_Mode value */
USART1->CR1 |= (USART_CR1_RE | USART_CR1_TE);
/*---------------------------- USART CR3 Configuration -----------------------*/
/* Clear CTSE and RTSE bits */
USART1->CR3 &= ~(USART_CR3_CTSE | USART_CR3_RTSE);
/* Configure the USART HFC -------------------------------------------------*/
/* Set CTSE and RTSE bits according to USART_HardwareFlowControl value */
/* Write to USART CR3 */
/*---------------------------- USART BRR Configuration -----------------------*/
/* Configure the USART Baud Rate -------------------------------------------*/
/* Determine the integer part */
/* Determine the fractional part */
USART1->BRR = 0xd0;
/*---------------------------- USART Enable ----------------------------------*/
/* USART Enable Configuration */
USART1->CR1 |= USART_CR1_UE;
/*---------------------------- USART DATA output -----------------------------*/
/* USART DATA Transmission */
}
실험 결과
terminal 에서 해당하는 Port Number 와 코드에서 설정한 값들을 맞게 옵션을 선택한 뒤 Connect 를 눌려 결과를 확인한다.
terminal 을 통해 확인한 결과.
오실로스코프로 확인한 결과.
문자의 ASCII 값에 맞는 binary 형태의 파형을 얻을 수 있다. 값은 오른쪽에서 왼쪽 방향으로 읽어야 한다.
그림을 보면, 01010111(2) 값을 나타냄으로 이 값은 10진수로 87 즉, “W” 에 해당된다.
결론
System 내부/외부 클럭을 조절하여 원하는 클럭을 생성하고 이를 이용하여 UART(비동기식) 통신을 할 수 있었다. eclipse 를 통해 보드에서 생성된 코드를 terminal 이라는 실행 프로그램으로 확인할 수 있었다. 그리고 오실로스코프로 클럭을 확인하고 보낸 문자에 해당하는 ASCII 값을 binary 형태의 파형으로 볼 수 있어 쉽게 확인할 수 있었다. 이때 Start bit가 0, Stop bit가 1, 왼쪽이 낮은 비트이다. 이번 실험에선 clock tree 를 많이 다루어 tree 구조, 내부의 multiplexer, divider, mco 등의 기능 그리고 클럭의 흐름을 잘 이해할 수 있었다. 또, 새롭게 알게된 UART, USART 의 용도와 차이점을 알 수 있었다.
전체 소스
#include "stm32f10x_gpio.h"
#include "stm32f10x.h"
#include "stm32f10x_usart.h"
#include <vector>
#include <string>
using namespace std;
#define STM32F10X_CL
void delay(){
for(int i = 0; i < 1000000; i++);
}
void SysInit(void) {
/* Set HSION bit */
/* Internal Clock Enable */
RCC->CR |= (uint32_t) 0x00000001;
/* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
RCC->CFGR &= (uint32_t) 0xF0FF0000;
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t) 0xFEF6FFFF;
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t) 0xFFFBFFFF;
/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
RCC->CFGR &= (uint32_t) 0xFF80FFFF;
/* Reset PLL2ON and PLL3ON bits */
RCC->CR &= (uint32_t) 0xEBFFFFFF;
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x00FF0000;
/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000;
}
void SetSysClock(void)
{
volatile uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* Enable HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE;
/* Flash 0 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0;
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV2;
/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV2;
/* PCLK1 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1;
/* Configure PLLs ------------------------------------------------------*/
/* PLL configuration: PLLCLK = ???? */
// HSE = 25, PREDIV1_div2 : /2 , PLLMULL4: *4
RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_PLLMULL4);
/* PLL2 configuration: PLL2CLK = ???? */
/* PREDIV1 configuration: PREDIV1CLK = ???? */
// HSE, MUL_0,
RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |
RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL12 |
RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);
/* Enable PLL2 */
RCC->CR |= RCC_CR_PLL2ON;
/* Wait till PLL2 is ready */
while((RCC->CR & RCC_CR_PLL2RDY) == 0)
{
}
/* Enable PLL */
RCC->CR |= RCC_CR_PLLON;
/* Wait till PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
RCC->CFGR |= (uint32_t)RCC_CFGR_MCO_2;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else
{ /* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
}
void UartInit(void) {
/*---------------------------- RCC Configuration -----------------------------*/
/* GPIO RCC Enable */
/* USART RCC Enable */
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
/* USART Pin Configuration */
GPIOA->CRH = (GPIO_CRH_MODE8 | GPIO_CRH_CNF8_1 | GPIO_CRH_MODE9 | GPIO_CRH_CNF9_1);
/*---------------------------- USART CR2 Configuration -----------------------*/
/* Clear STOP[13:12] bits */
/* Configure the USART Stop Bits, Clock, CPOL, CPHA and LastBit ------------*/
/* Set STOP[13:12] bits according to USART_StopBits value */
USART1->CR2 &= ~(USART_CR2_STOP | USART_CR2_CPOL | USART_CR2_CPHA);
/*---------------------------- USART CR1 Configuration -----------------------*/
/* Clear M, PCE, PS, TE and RE bits */
USART1->CR1 &= ~(USART_CR1_M | USART_CR1_PCE | USART_CR1_PS | USART_CR1_TE | USART_CR1_RE);
/* Configure the USART Word Length, Parity and mode ----------------------- */
/* Set the M bits according to USART_WordLength value */
/* Set PCE and PS bits according to USART_Parity value */
/* Set TE and RE bits according to USART_Mode value */
USART1->CR1 |= (USART_CR1_RE | USART_CR1_TE);
/*---------------------------- USART CR3 Configuration -----------------------*/
/* Clear CTSE and RTSE bits */
USART1->CR3 &= ~(USART_CR3_CTSE | USART_CR3_RTSE);
/* Configure the USART HFC -------------------------------------------------*/
/* Set CTSE and RTSE bits according to USART_HardwareFlowControl value */
/* Write to USART CR3 */
/*---------------------------- USART BRR Configuration -----------------------*/
/* Configure the USART Baud Rate -------------------------------------------*/
/* Determine the integer part */
/* Determine the fractional part */
USART1->BRR = 0xd0;
/*---------------------------- USART Enable ----------------------------------*/
/* USART Enable Configuration */
USART1->CR1 |= USART_CR1_UE;
/*---------------------------- USART DATA output -----------------------------*/
/* USART DATA Transmission */
}
void SendData(int data){
while(!(USART1->SR&USART_SR_TXE));
USART1->DR = data & 0xFF;
//데이터를 다 보내는 것을 기다린다.
// while(!USART1->SR | !USART_SR_TXE));
}
int main() {
RCC->APB2ENR = (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN); //0x3c;
GPIOA->CRH = GPIO_CRH_MODE8 | GPIO_CRH_CNF8_1;
GPIOC->CRH = (GPIO_CRH_MODE8 | GPIO_CRH_MODE9 | GPIO_CRH_MODE10 | GPIO_CRH_MODE11);
GPIOC->IDR = 0x00000000;
SysInit();
SetSysClock();
UartInit();
/*---------------------------- USART DATA output -----------------------------*/
// char* names[] = {"hks", "lhw", "hsh"};
int count = 0;
while (1) {
// if((~GPIOC->IDR & (GPIO_IDR_IDR2) ) ){
// for(int i=0; i<strlen(names[count]); i++){
// SendData(names[count][i]);
// }
// delay();
// count++;
//
// if(count >= 3){
// count = 0;
// }
// }
if((~GPIOC->IDR & (GPIO_IDR_IDR2)) && count == 0){
SendData('W');
SendData('e');
SendData('d');
SendData('T');
SendData('e');
SendData('a');
SendData('m');
SendData('7');
SendData(' ');
SendData('H');
SendData('K');
SendData('S');
SendData(' ');
delay();
count = (count+1)%3;
}
if((~GPIOC->IDR & (GPIO_IDR_IDR2)) && count == 1){
SendData('W');
SendData('e');
SendData('d');
SendData('T');
SendData('e');
SendData('a');
SendData('m');
SendData('7');
SendData(' ');
SendData('H');
SendData('H');
SendData('H');
SendData(' ');
delay();
count = (count+1)%3;
}
if((~GPIOC->IDR & (GPIO_IDR_IDR2)) && count == 2){
GPIOD->BSRR = GPIO_BSRR_BS2;
SendData('W');
SendData('e');
SendData('d');
SendData('T');
SendData('e');
SendData('a');
SendData('m');
SendData('7');
SendData(' ');
SendData('L');
SendData('H');
SendData('W');
SendData(' ');
delay();
count = (count+1)%3;
}
}
}