我是靠谱客的博主 坚强茉莉,这篇文章主要介绍UART(一)裸机编程,现在分享给大家,希望可以做个参考。

S5PV210 包含 4 个异步收发器(UART),提供 4 个独立的异步串行输入/输出(I/O)端口。所有端口可工作于中断模式或 DMA 模式。提供高达 3Mbps 的位速率。每个 UART 包含 2 个 FIFO 用于接收和发送数据。具有可编程的波特率、红外收发、1 位或 2 位停止位、5~8 位数据位、校验。

其中UART1和UART2也被转为232接口
S5PV210的uart结构图如下

数据发送:要发送的数据帧是可编程的。它包含 1 位起始位, 5~8 位数据位, 1 个可选校验位, 1
或 2 位停止位,这些都通过 ULCONn 寄存器来设置。在 FIFO 模式下发送器将要发送的数据发送给 Tx FIFO,
在非 FIFO 模式下,发送器将要发送的数据发送给 Tx 保持寄存器。
数据接收:和数据发送类似。

串口编程操作步骤如下
1、配置时钟,选择时钟源。
2、配置ULCONn寄存器,设置模式,校验位,停止位和数据位
3、配置UCONn寄存器,设置数据接收和发送模式,以及时钟源等
4、配置UFCONn寄存器,启用或禁用FIFO
5、配置UBRDIVnUDIVSLOTn寄存器:计算波特率
6、发送数据UTXHn:查询状态寄存器UTRSTATn等待发送器为空,将要发送的8位数据赋给发送缓存寄存器UTXHn
7、接收数据URXHn:查询状态寄存器UTRSTATn等待接收缓冲区有数据可读,从接收缓存寄存器URXHn中取出数据

波特率计算

计算出来的小数部分需要进行查表

start.S

复制代码
1
2
3
4
5
6
.global _start /* 声明一个全局的标号 */ _start: bl uart_init /* 串口初始化 */ bl main /* 跳转到C函数去执行 */ halt: b halt /* 死循环 */

uart.c

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#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; } static void uart_send_byte(unsigned char byte) { while (!(UTRSTAT0 & (1 << 2))); /* 等待发送缓冲区为空 */ UTXH0 = byte; /* 发送一字节数据 */ } static unsigned char uart_recv_byte() { while (!(UTRSTAT0 & 1)); /* 等待接收缓冲区有数据可读 */ return URXH0; /* 接收一字节数据 */ } void putchar(int c) { uart_send_byte(c); /* 如果只写'n',只是换行,而不会跳到下一行开头 */ if (c == 'n') uart_send_byte('r'); } int getchar() { int c; c = uart_recv_byte(); return c; } void puts(char *str) { char *p = str; while (*p) putchar(*p++); putchar('n'); }

main.c

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#define GPC0CON *((volatile unsigned int *)0xE0200060) #define GPC0DAT *((volatile unsigned int *)0xE0200064) int main() { int c; GPC0CON &= ~(0xFF << 12); GPC0CON |= 0x11 << 12; // 配置GPC0_3和GPC0_4为输出 GPC0DAT &= ~(0x3 << 3); // 熄灭LED1和LED2 puts("UART Test in S5PV210"); puts("1.LED1 Toggle"); puts("2.LED2 Toggle"); puts("Please select 1 or 2 to Toggle the LED"); while (1) { c = getchar(); // 从串口终端获取一个字符 putchar(c); // 回显 putchar('r'); if (c == '1') GPC0DAT ^= 1 << 3; // 改变LED1的状态 else if (c == '2') GPC0DAT ^= 1 << 4; // 改变LED2的状态 } return 0; }

Makefile

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
uart.bin: start.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 $@ -fno-builtin %.o : %.S arm-linux-gcc -c $< -o $@ clean: rm *.o *.elf *.bin *.dis

实验现象: 按数字 1 改变 LED1 的状态;按数字 2 改变 LED2 的状态。

由于我们在 uart.c 中使用了和 C 库同名的函数: putchar、 getchar、 puts,为了不和 C 库中的同名函
数发送冲突,需要给 gcc 加一个选项-fno-builtin,不使用内建函数。

问:为什么我们没有进行时钟配置相关的操作
答:因为 S5PV210 在启动时,运行 iROM 里的代码已经为我们初始化了时钟,其中 PCLK=66MHz

最后

以上就是坚强茉莉最近收集整理的关于UART(一)裸机编程的全部内容,更多相关UART(一)裸机编程内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(97)

评论列表共有 0 条评论

立即
投稿
返回
顶部