uart
串口通信
学习一般步骤:
1.弄懂原理
a.通信格式,数据
串口控制器与处理器相连接,然后控制器与数据线相连接,有9根线,一根rx,一根tx
怎么保证数据没有错,两点决定:
a.通信速率,必须保证接收/发送双方的发送速率是一致的(波特率一致),否则会导致接收的数据不对,还是很好理解的;115200是每秒传输的位数1s传递10k数据
b.数据位(实际数据,长度位8);
校验位(校验前边数据是否正确,1位),保证前边的数据尽可能的正确,奇校验与偶校验,用一个位去校验前边的数据是否正确,奇校验(O)查前边逻辑0是奇数个就是1,偶数个就是0,同理偶校验(E)就反过来,偶数个就是1,奇数个就是0,如果有一位出错就可以校验出来;不校验(N)
停止位(1位或者2位),就是传一组数据,停一下让你响应一会,然后再传下一组数据,停止位长度,1位停止位,就是空出一个传一位的时间让对方响应
b.原理图
关心2,3管脚,RXD0,TXD0就是数据收发线,然后连接到一个芯片上SP3232EEN芯片,就是电压转换的,经过这个芯片转换为TTL电平,ARM是TTL电平,原理图芯片两边,左边是输入芯片的关键,经过芯片右边的就是转换电平之后的管脚,然后经过找原理图找到核心板的对应管脚,然后根据核心板的原理图中的管脚,在芯片手册中找到这个管脚的相关寄存器控制,XuRXD0/GPA0
c.怎么通过控制器控制
复用管脚,先配置成串口,然后再看串口控制器的控制方法
1.先配置GPA0CON[1]寄存器配置成0010就是uart的发送管脚,先配置GPA0CON[0]寄存器配置成0010就是uart的接收管脚,0与1代表着管脚
2.GPIO复用其他功能的时候,上下拉电阻通常是关掉的,所以GPA0PUD[0]与GPA0PUD[1]应该全为0,就把上下拉电阻关掉了
以上已经配置成UART的功能了
然后根据芯片手册找到UART的章节,看看具体的控制器编程方法
相关寄存器:
ULCON0(线控寄存器,控制线路上数据传输的格式):
bit0~1(Word Length):设定串口传递的数据长度
bit2(Number of Stop Bit):设定串口传递停止位个数
bit3~5(Parity Mode):设定串口传递校验模式
bit6(Infrared Mode):设定串口传递红外模式(通信媒介是否是采用红外线的模式),是否使用红外模式
UCON0(控制器,控制器本身的控制寄存器):
bit0~1(Receive Mode):接收模式
01:中断方式/查询方式(polling方式,就是死循环读数据)
10:DMA模式(直接内存访问,能够访问内存,通常内存只能由CPU读写,但是DMA模式,就是让DMA控制器来访问内存,这样就不需要CPU去亲自搬运数据了,然后加快了速率,CPU会告诉DMA控制器,从哪搬运数据,板运数据到哪,搬运多少数据,搬运完成后DMA通知CPU,已经搬运到内存中了,CPU使用吧,通常用于大数据的传输的场景)
bit2~3(Transmit Mode):传输模式
方式与接收方式相同
bit4(Send Break Signal ):特殊场景下,发送这一位,会打断原有的通信
00:正常模式
bit5(Loop-back Mode):回路模式,自己发走的数据,对方不接收,自己再接收一下,用回路模式测试,硬件是否发送接收有无问题
00:正常模式
bit6~9中断相关的,6出错是否触发中断,7超时触发,8中断触发方式
bit10(Clock Selection):数字电路中的时钟是驱动一切东西的根源,类似于人的心跳,时钟的来源是时钟控制器,负责发给每一个模块时钟
时钟控制器提供两个时钟:PCLK与SCLK_UART,uboot决定了时钟是哪个,uboot初始化的时候选择时钟,初始化内存,等等工作
UFCON0(UART的FIFO):等价于发送/接收缓冲,,先进先出,有FIFO的,缓冲满了后一起发送出去或接收过来,如果不是FIFO,就来一个数据发一次或接收一次,有FIFO在传输大数据的时候还是可以提高效率的
配置波特率的寄存器:
UBRDIV0(配置波特率,主要配置波特率的寄存器):这个值芯片手册会提供具体的算法,首先要明确咱们所要的波特率,作为一个参数带入公式计算一下就得到这个值,将这个值给到这个寄存器的低16位
UDIVSLOT0(微调的寄存器,频率特别低的时候可以不配置这个寄存器):将UBRDIV0这个值的小数部分*16,将这个整数与芯片手册提供的表格进行对比,得出一个数,给到这个寄存器就是微调的数据
发送数据:
通过处理器(CPU),把数据送给Transmit Buffer Register,之后硬件会将数据从这个寄存器中给到Transmit shift移位器,然后这个移位器将数据给到TX管脚发给外设,这个移位器是由波特率产生器控制,然后移位器按照设定的波特率来发送速率
由于串口控制器的波特率低于处理器的速度,所以发送出去的速度与CPU给给到寄存器的速度相比,要满,所以要查询寄存器是否为空,需要不停查询,直到空了,再往里边放数据,这就是实际项目中,通常发出串口数据后,要加延时,这个查询就引出UTRSTAT0这个寄存器,需要查询这个寄存器,bit2位,Transmitter empty 为1代表整个寄存器空了,bit1位,Transmit buffer empty 为1代表buffer为空,可以往里存数据,发送数据有UTXH0这个寄存器,0~7位,就存储要发送的数据
接收数据:
方法同上
2.实验,写一个shell,实现收发字符
start.s
.global start@为了让.c看得到
start:
bl uart_run @没有赋值,说明uart_run没有参数
b . @b .是死循环
uart.c
char shell_buffer[256];
void uart_init(void){
配置寄存器,就是操作地址
}
char getc(void){
volatile int *b = (int *)0xe2900010;
//1.查看状态
while(!(*b&0x1)){
//2.读出字符
b = (int *)0xe2900024;
return (char)*b;
}
}
void putc(char a){
volatile int *b = (int *)0xe2900010;
//1.查看状态
while(!(*b&0x2)){
//2.写入字符
b = (int *)0xe2900024;
(char)*b = a;
}
}
void puts(char* s){
}
void gets(void){
int i = 0;
while(1){
if((shell_buffer[i] != '\r')&&(i<256)){ //输入回车结束,'\r'是回车
shell_buffer[i] = getc(); //获取输入的指令
putc(shell_buffer[i]); //这里是为了在终端显示
i++;
}else{
if(shell_buffer[i] == '\r'){
shell_buffer[i] = '\0';
}else{
/*do nothing*/
}
break;
}
}
}
void parse_cmd(void){
char cmd_buffer[5] = "test";
for(int i=0;((shell_buffer[i] != '\0')&&(i<5));i++){
if(shell_buffer[i] != cmd_buffer[i]){
puts("cmd is error");
break;
}else{
do_test_cmd();//硬盘上的一段程序,通常需要shell进程去做这个函数的处理
}
}
}
void uart_run(void){
uart_init();
while(1){
char a = getc();
putc(a);
puts("##");
gets();
parse_cmd();
}
}
shell结构:首先是一个循环,第一步获取输入的命令,第二步分析命令,第三步执行命令,第四步结束运行等待下次输入的命令
shell有提示符:实现打印字符串,但是不打回车
nandflash存储器编程方法
时钟