【嵌入式】——arm裸机开发 step by step 之 串口通信
一、在使用S5PV210的串口发送和接收的时候,首先要对S5PV210的串口进行配置,我们使用轮询方式时的配置有哪些?
1、配置GPIO,使对应管脚作为串口的发送和接收管脚
GPA0 0 1 管脚 2 3 可以配置,但我们没有使用
GPA0CON寄存器[7:4][3:0] 0x22
GPA0PUD寄存器[3:0] 0 禁止上下拉电阻
2、配置串口单元本身寄存器
ULCON0 0xE2900000
数据位:8位
停止位:1位
校验位:无
使用的正模式,非红外。
3、UCON0 0xE2900004
串口的收发模式:轮训
串口的时钟使用的PCLK UFCON0 0xE2900008
禁止FIFO
UMCON0 0xE290000C
禁止Modem
UBRDIV0 0xE2900028
UDIVSLOT0 0xE290002C
UBRDIV0 = PCLK或者SCLK_UART/波特率/16 - 1 的整数部分
UDIVSLOT0 查表,怎么查?
PCLK或者SCLK_UART/波特率/16 - 1 的小数部分 * 16 取整
查表
PCLK=66500000
波特率是115200
UBRDIV0 = 35
UDIVSLOT0 = 0x0080
发送数据流程(轮询方式)
uart0_putc()
判断UTRSTAT0的BIT1,如果BIT1是0等待如果BIT1是1,就把要发送的一个字节数据写到发送寄存器(UTXH0,0xE2900020)
接收数据流程(轮询方式)
uart0_getc
判断UTRSTAT0的BIT0,如果BIT0是0等待如果BIT0是1,从URXH0 0xE2900024读取一个字节的数据。
编程时:
0xE2900000地址单元写3
0xE2900004地址单元写5
0xE2900008地址单元写0
0xE290000C地址单元写0
0xE2900028地址单元写35
0xE290002C地址单元写0x80
uart.h
1 #ifndef _UART_H_ 2 #define _UART_H_ 3 4 #define GPA0CON (*(volatile unsigned int *)0xE0200000) 5 #define GPA0PUD (*(volatile unsigned int *)0xE0200008) 6 7 #define ULCON0 (*(volatile unsigned int *)0xE2900000) 8 #define UCON0 (*(volatile unsigned int *)0xE2900004) 9 #define UFCON0 (*(volatile unsigned int *)0xE2900008) 10 #define UMCON0 (*(volatile unsigned int *)0xE290000C) 11 #define UTRSTAT0 (*(volatile unsigned int *)0xE2900010) 12 #define UTXH0 (*(volatile unsigned int *)0xE2900020) 13 #define URXH0 (*(volatile unsigned int *)0xE2900024) 14 #define UBRDIV0 (*(volatile unsigned int *)0xE2900028) 15 #define UDIVSLOT0 (*(volatile unsigned int *)0xE290002C) 16 17 #define PCLK (66500000) 18 19 //函数原型声明 20 extern void uart0_init(void); 21 extern void uart0_puts(const char *); 22 extern void uart0_putc(char); 23 extern char uart0_getc(void); 24 extern void uart0_gets(char *,int); 25 26 #endif // _UART_H_
uart.c
1 #include "uart.h" 2 3 //初始化串口寄存器 4 void uart0_init(void){ 5 //配置GPIO口 根据CPU 手册中设置下面的寄存器 6 //GPA0CON GPA0PUD 7 ULCON0 = 3; 8 UCON0 = 5; 9 UFCON0 = 0; 10 UMCON0 = 0; 11 UBRDIV0 = 35; 12 UDIVSLOT0 = 0x0080; 13 GPA0CON = 34; 14 GPA0PUD = ~0xF; 15 } 16 //发送一个字符 17 void uart0_putc(char c){ 18 //判断状态位 19 while(!(UTRSTAT0 & (1<<1))); 20 //发送字符 21 UTXH0 = c; 22 } 23 //接收一个字符 24 char uart0_getc(void){ 25 while(!(UTRSTAT0 & 1)); 26 27 return URXH0; 28 } 29 30 //接收一串字符 31 void uart0_gets(char *str,int len){ 32 char* tmp = str; 33 int in = len; 34 //int i; 35 while(--len){ 36 *tmp = uart0_getc(); 37 if(*tmp == '\r'){ 38 uart0_putc('\n'); //若此处为 \r 则不会输出,若为 \n 则在下一行跳跃输出字符的长度,然后输出字符串 39 uart0_putc('\r'); 40 break; 41 } 42 if(*tmp == 127){ //127 是 ubuntu下 kermit软件中的 BACKSPACE按键 需要实现的效果就是当按下回车键的时候终端的上一个数据会被删掉, 43 len++; //由于此分支的 127 输入到了 *tmp 中,此时的127是无用的,所以要进行 len++ ,但是有一个问题,我们的退格的目的是删除上一个字母,所以127的上一个字符也没用了,需要对len做两次自加进行还原 但是又出现一个问题,如果已经删到第0个元素就不能再自加两次了,这样会造成 len 越来越大。因此要在下面做一个判断 44 if(len < in){ 45 len++; 46 } 47 if(tmp == str){ 48 continue; 49 } 50 51 uart0_putc('\b'); 52 uart0_putc(' '); 53 uart0_putc('\b'); 54 --tmp; 55 continue; 56 } 57 uart0_putc(*tmp); 58 tmp++; 59 } 60 *tmp = 0; 61 } 62 63 //发送一串字符 64 void uart0_puts(const char *str){ 65 if(str == 0){ 66 return; 67 } 68 while(*str){ 69 uart0_putc(*str); 70 if(*str == '\n'){ 71 uart0_putc('\r'); 72 } 73 str++; 74 } 75 }
main.c
1 #include "uart.h" 2 3 void main(void){ 4 char val[30]; 5 uart0_init(); 6 while(1){ 7 8 //uart0_gets(val); 9 uart0_puts("\nstart\n"); 10 uart0_gets(val,30); 11 uart0_puts(val); 12 13 // uart0_puts(uart0_getc()); 14 } 15 }
makefile
1 PROG=uart 2 OBJS=main.o uart.o 3 4 AS=arm-linux-as 5 CC=arm-linux-gcc 6 LD=arm-linux-ld 7 OBJCOPY=arm-linux-objcopy 8 AFLAGS=-march=armv5te 9 CFLAGS=-march=armv5te -nostdlib 10 LDFLAGS=-nostartfiles -nostdlib -Ttext=0x20008000 -e main 11 12 $(PROG): $(OBJS) 13 $(LD) $(LDFLAGS) -o $(PROG) $(OBJS) 14 $(OBJCOPY) -O binary $(PROG) $(PROG).bin 15 cp uart.bin /tftpboot 16 17 %.o:%.s 18 $(AS) $(AFLAGS) -o $@ $< 19 20 %.o:%.c 21 $(CC) $(CFLAGS) -c -o $@ $< 22 clean: 23 @rm -vf $(OBJS) $(PROG) $(PROG).bin
二、arm9 TQ2440
这款 arm9 的串口通信跟 上面的 S5PV210相似:
uart.h
1 #ifndef __UART_H__ 2 #define __UART_H__ 3 4 //gpio 5 #define GPHCON (*(volatile unsigned long*)0x56000070) 6 #define GPHUP (*(volatile unsigned long*)0x56000078) 7 8 //uart0 register 9 #define ULCON0 (*(volatile unsigned long*)0x50000000) //设置传输格式 10 #define UCON0 (*(volatile unsigned long*)0x50000004) //选择时钟源和中断方式 11 #define UFCON0 (*(volatile unsigned long*)0x50000008) //是否使用FIFO 12 #define UMCON0 (*(volatile unsigned long*)0x5000000C) //流量控制模式 13 #define UTRSTAT0 (*(volatile unsigned long*)0x50000010) //设置状态寄存器 [0] 当接收到数据时,此位被自动设为1 [1] 当发送缓冲区中没有数据时,此位被设置为1 [2] 当发送缓冲区中没有数据,并且最后一个数据也已经发送出去时,此位被自动设为1 14 #define UBRDIV0 (*(volatile unsigned long*)0x50000028) //设置波特率 15 16 //收发数据寄存器 17 #define UTXH0 (*(volatile unsigned long*)0x50000020) //发数据 18 #define URXH0 (*(volatile unsigned long*)0x50000024) //收数据 19 #define UTXH0B (*(volatile unsigned long*)0x50000023) //发数据 20 #define URXH0B (*(volatile unsigned long*)0x50000027) //收数据 21 22 //函数声明 23 extern void uart0_init(void); 24 extern char uart0_getc(void); 25 extern void uart0_putc(char); 26 extern void uart0_gets(char *,int); 27 extern void uart0_puts(char *); 28 29 #endif
uart.c
1 #include "uart.h" 2 3 void uart0_init(void){ 4 //配置 gpio 管脚 5 GPHCON &= ~0xf0; 6 GPHCON |= 0xa0; 7 //GPHUP &= 0x0c; //设置 gph2 gph3 内部上拉 8 GPHUP &= ~0x6; //设置 gph2 gph3 上下拉电阻禁止 9 10 ULCON0 = 3; //数据位 8位 一个停止位 无校检 正常模式 11 UCON0 = 5; //查询方式 12 UFCON0 = 0; //禁止FIFO 13 UMCON0 = 0; //禁止流量控制 14 UBRDIV0 = 50000000/(115200*16)-1; //26 15 } 16 17 char uart0_getc(void){ 18 char ch; 19 while(!(UTRSTAT0 & 1)); 20 //ch = URXH0; 21 return URXH0; 22 } 23 24 void uart0_putc(char ch){ 25 while(!(UTRSTAT0 & (1<<2))); 26 UTXH0 = ch; 27 } 28 29 void uart0_gets(char* ch,int len){ 30 int flag = len; 31 while(--len){ 32 *ch = uart0_getc(); 33 if(*ch == '\r'){ 34 uart0_putc(*ch); 35 break; 36 } 37 if(*ch == 8){ //在dnw中 BACK SPACE按键是8 在 kermit下是127 38 len++; //此次循环接收的是退格符号所以无论如何len都要自加 39 if(flag == len){ //为了防止回删到开头,所以做个限制 40 continue; 41 } 42 uart0_putc('\b'); 43 uart0_putc(' '); 44 uart0_putc('\b'); 45 len++; 46 ch--; 47 continue; 48 } 49 uart0_putc(*ch); 50 ch++; 51 } 52 *ch = 0; 53 54 } 55 void uart0_puts(char* ch){ 56 while(*ch){ 57 uart0_putc(*ch); 58 //如果是在kermit 下需要用到下面代码; 59 /* 60 if(*ch == '\n'){ 61 uart0_putc('\r'); 62 } 63 */ 64 ch++; 65 } 66 67 }
main.c
1 #include "uart.h" 2 3 void delay(void); 4 5 int main(void){ 6 uart0_init(); 7 8 uart0_puts("ok start:\n"); 9 while(1){ 10 char ch[30]; 11 uart0_puts("netboy# "); 12 uart0_gets(ch,5); 13 uart0_puts(ch); 14 uart0_puts("\n"); 15 } 16 return 0; 17 } 18 19 void delay(void){ 20 int i; 21 for(i = 0; i < 0xf0000; i++){ 22 ; 23 } 24 }
uart.lds
1 SECTIONS 2 { 3 . = 0x30000000; 4 .text :{ 5 main.o (.text); 6 *.o (.text); 7 } 8 .data :{ 9 *.o (.data); 10 } 11 __bss_start = .; 12 .bss :{ 13 *.o (.bss); 14 } 15 __end = .; 16 };
Makefile
1 PROG=uart 2 OBJS=main.o uart.o 3 4 CC=arm-linux-gcc 5 LD=arm-linux-ld 6 OBJCOPY=arm-linux-objcopy 7 AFLAGS=-march=armv5te 8 CFLAGS=-march=armv5te -nostdlib 9 LDFLAGS=-nostartfiles -nostdlib -Ttext=0x30000000 -e main 10 11 $(PROG): $(OBJS) 12 $(LD) $(LDFLAGS) -o $(PROG) $(OBJS) 13 $(OBJCOPY) -O binary $(PROG) $(PROG).bin 14 cp uart.bin /mnt/hgfs/linux_share/tq2440/ 15 %.o:%.c 16 $(CC) $(CFLAGS) -c -o $@ $< 17 18 clean: 19 @rm -vf $(PROG) $(OBJS) $(PROG).bin