TQ210裸机编程(6)——UART(1)
S5PV210包含4个异步收发器(UART),提供4个独立的异步串行输入/输出(I/O)端口。所有端口可工作于中断模式或DMA模式。提供高达3Mbps的位速率。每个UART包含2个FIFO用于接收和发送数据。
具有可编程的波特率、红外收发、1位或2位停止位、5~8位数据位、校验。
UART框图如下:
数据发送:要发送的数据帧是可编程的。它包含1位起始位,5~8位数据位,1个选项校验位,1或2位停止位,这些都通过ULCONn寄存器来设置。在FIFO模式下发送器将要发送的数据发送给Tx FIFO,在非FIFO模式下,发送器将要发送的数据发送给Tx保持寄存器。
数据接收:和数据发送类似。
串口编程操作步骤:
1、配置时钟,选择时钟源
2、配置ULCONn寄存器:设置数据位、停止位校验位、模式
3、配置UCONn寄存器:设置数据接收和发送模式、时钟源
3、设置UFCONn:启用或静止FIFO
4、配置UBRDIVn和UDIVSLOTn:计算波特率
5、发送数据:等待发送器为空,将要发送的8位数据赋给发送缓存寄存器UTXHn
6、接收数据:等待接收缓冲区有数据可读,从接收缓存寄存器URXHn中取出数据。
波特率计算:
DIV_VAL = UBRDIVn + (num of 1's in UDIVSLOTn)/16
DIV_VAL = (PCLK / (bps x 16)) −1
或者
DIV_VAL = (SCLK_UART / (bps x 16)) −1
比如配置波特率为115200bps,时钟源选择PCLK=66MHz
DIV_VAL = (66000000/(115200 x 16))-1 = 35.8 - 1 = 34.8
UBRDIV0 = 34 (DIV_VAL的整数部分)
(num of 1's in UDIVSLOTn)/16 = 0.8 (DIV_VAL的小数部分)
(num of 1's in UDIVSLOTn) = 12
UDIVSLOT0 = 0xDDDD (查表)
实验效果:
按数字1则改变LED1的状态;按数字2改变LED2的状态。
代码:
start.S
.global _start /* 声明一个全局的标号 */ _start: bl clock_init /* 时钟初始化 */ bl uart_init /* 串口初始化 */ bl main /* 跳转到C函数去执行 */ halt: b halt
clock.c
#define APLLCON0 *((volatile unsigned int *)0xE0100100) #define MPLLCON *((volatile unsigned int *)0xE0100108) #define EPLLCON0 *((volatile unsigned int *)0xE0100110) #define VPLLCON *((volatile unsigned int *)0xE0100120) #define CLK_SRC0 *((volatile unsigned int *)0xE0100200) #define CLK_DIV0 *((volatile unsigned int *)0xE0100300) #define CLK_DIV1 *((volatile unsigned int *)0xE0100304) #define CLK_DIV2 *((volatile unsigned int *)0xE0100308) #define CLK_DIV3 *((volatile unsigned int *)0xE010030C) void clock_init() { /* 1、设置PLL_LOCK寄存器(这里使用默认值) */ /* 2、设置PLL_CON寄存器(使用芯片手册推荐的值) */ APLLCON0 = (1 << 0) | (3 << 8) | (125 << 16) | (1 << 31); /* FOUTAPLL = 1000MHz */ MPLLCON = (1 << 0) | (12 << 8) | (667 << 16) | (1 << 31); /* FOUTMPLL = 667MHz */ EPLLCON0 = (1 << 0) | (12 << 8) | (667 << 16) | (1 << 31); /* FOUTEPLL = 96MHz */ VPLLCON = (3 << 0) | (6 << 8) | (108 << 16) | (1 << 31); /* FOUTVPLL = 54MHz */ /* 3、选择PLL为时钟输出 */ /* MOUT_MSYS = SCLKAPLL = 1000MHz ** MOUT_DSYS = SCLKMPLL = 667MHz ** MOUT_PSYS = SCLKMPLL = 667MHz */ CLK_SRC0 = (1 << 0) | (1 << 4) | (1 << 8) | (1 << 12); /* 4、设置系统时钟分频值 */ /* freq(ARMCLK) = MOUT_MSYS / (APLL_RATIO + 1) = 1000MHz / (0 + 1) = 1000MHz ** freq(HCLK_MSYS) = ARMCLK / (HCLK_MSYS_RATIO + 1) = 1000MHz / (4 + 1) = 200MHz ** freq(PCLK_MSYS) = HCLK_MSYS / (PCLK_MSYS_RATIO + 1) = 200MHz / (1 + 1) = 100MHz ** freq(HCLK_DSYS) = MOUT_DSYS / (HCLK_DSYS_RATIO + 1) = 667 / (3 + 1) = 166MHz ** freq(PCLK_DSYS) = HCLK_DSYS / (PCLK_DSYS_RATIO + 1) = 166 / (1 + 1) = 83MHz ** freq(HCLK_PSYS) = MOUT_PSYS / (HCLK_PSYS_RATIO + 1) = 667 / (4 + 1) = 133MHz ** freq(PCLK_PSYS) = HCLK_PSYS / (PCLK_PSYS_RATIO + 1) = 133 / (1 + 1) = 66MHz */ CLK_DIV0 = (0 << 0) | (4 << 8) | (1 << 12) | (3 << 16) | (1 << 20) | (4 << 24) | (1 << 28); }
uart.c
#define GPA0CON *((volatile unsigned int *)0xE0200000) #define ULCON0 *((volatile unsigned int *)0xE2900000) #define UCON0 *((volatile unsigned int *)0xE2900004) #define UFCON0 *((volatile unsigned int *)0xE2900008) #define UTRSTAT0 *((volatile unsigned int *)0xE2900010) #define UTXH0 *((volatile unsigned int *)0xE2900020) #define URXH0 *((volatile unsigned int *)0xE2900024) #define UBRDIV0 *((volatile unsigned int *)0xE2900028) #define UDIVSLOT0 *((volatile unsigned int *)0xE290002C) /* ** UART0初始化 */ void uart_init() { /* ** 配置GPA0_0为UART_0_RXD ** 配置GPA0_1为UART_0_TXD */ GPA0CON &= ~0xFF; GPA0CON |= 0x22; /* 8-bits/One stop bit/No parity/Normal mode operation */ ULCON0 = 0x3 | (0 << 2) | (0 << 3) | (0 << 6); /* Interrupt request or polling mode/Normal transmit/Normal operation/PCLK/*/ UCON0 = 1 | (1 << 2) | (0 << 10); /* 静止FIFO */ UFCON0 = 0; /* ** 波特率计算:115200bps ** PCLK = 66MHz ** DIV_VAL = (66000000/(115200 x 16))-1 = 35.8 - 1 = 34.8 ** UBRDIV0 = 34 (DIV_VAL的整数部分) ** (num of 1's in UDIVSLOTn)/16 = 0.8 ** (num of 1's in UDIVSLOTn) = 12 ** UDIVSLOT0 = 0xDDDD (查表) */ UBRDIV0 = 34; UDIVSLOT0 = 0xDDDD; } void uart_send_byte(unsigned char byte) { while (!(UTRSTAT0 & (1 << 2))); /* 等待发送缓冲区为空 */ UTXH0 = byte; /* 发送一字节数据 */ } unsigned char uart_recv_byte() { while (!(UTRSTAT0 & 1)); /* 等待接收缓冲区有数据可读 */ return URXH0; /* 接收一字节数据 */ } void uart_send_string(char *str) { char *p = str; while (*p) uart_send_byte(*p++); }
main.c
#define GPC0CON *((volatile unsigned int *)0xE0200060) #define GPC0DAT *((volatile unsigned int *)0xE0200064) void delay(volatile unsigned int t) { volatile unsigned int t2 = 0xFFFF; while (t--) for (; t2; t2--); } int main() { int byte; GPC0CON &= ~(0xFF << 12); GPC0CON |= 0x11 << 12; // 配置GPC0_3和GPC0_4为输出 GPC0DAT &= ~(0x3 << 3); // 熄灭LED1和LED2 uart_send_string("\r\nUART Test in S5PV210\r\n"); while (1) { uart_send_string("\r\n1.LED1 Toggle\r\n"); uart_send_string("\r\n2.LED2 Toggle\r\n"); uart_send_string("\r\nPlease select 1 or 2 to Toggle the LED\r\n"); byte = uart_recv_byte(); uart_send_byte(byte); if (byte == '1') GPC0DAT ^= 1 << 3; // 改变LED1的状态 else if (byte == '2') GPC0DAT ^= 1 << 4; // 改变LED2的状态 } return 0; }
Makefile
uart.bin: start.o clock.o uart.o main.o arm-linux-ld -Ttext 0xD0020010 -o uart.elf $^ arm-linux-objcopy -O binary uart.elf $@ arm-linux-objdump -D uart.elf > uart.dis %.o : %.c arm-linux-gcc -c $< -o $@ %.o : %.S arm-linux-gcc -c $< -o $@ clean: rm *.o *.elf *.bin *.dis
程序烧写步骤见前面章节。
转载请注明来源:http://blog.csdn.net/zjhsucceed_329/