GNU ARM汇编--(七)s3c2440的串口控制
在配置完s3c2440的系统时钟后,我们来控制串口.之所以将串口放在这么靠前,是因为串口会给我们带来更多的信息.在没有串口的时候,uboot启动阶段只能靠led来显示一些信息.那么有了串口,debug就方便很多了.在工作中,一般情况下,很少有机会用jtag口加上昂贵的codeviser工具进行debug,也很少用gdb进行远程debug的,用的最多的还是打开debug的define来分析确定问题.
还是从s3c2440的datasheet开始:
s3c2440A的UART提供3个独立的异步串行IO口,每一个都支持中断和DMA.换句话来说,UART可以产生中断请求或DMA请求来在CPU和UART之间传输数据.UART在系统时钟下最高支持115200的波特率.如果使用UEXTCLK的话,UART可以工作在更高速的时钟下.每个UART通道都有来年各个64字节的FIFO,一个是接收用的,一个是发送用的.
UART包括可编程的波特率,IR发送/接受,一或两个停止位,5/6/7/8个数据位和奇偶校验.
每个UART包括一个波特率产生器,发送器,接收器和一个控制单元.波特率产生器可以由PCLK FCLK/n 或者UEXTCLK来锁定.发送器和接收器包含64字节的FIFO和数据移位器.数据首先写到FIFO中,然后在发送之前复制到发送器的移位器.最后数据移位通过TxDn数据发送管脚发送出去.同样的,接收的数据通过RxDn接收数据管脚移位进来,然后从移位器拷贝到FIFO中.
UART的框图如下:
值得说的是:在FIFO模式下,buffer寄存器的所有64个字节都被用作FIFO寄存器.而在非FIFO模式下,buffer寄存器只有一个字节被用于Holding寄存器.
串口的操作:
串口操作包括如下:数据发送,数据接收,中断产生,波特率产生,loopback模式,IR模式和自动流控制.
数据发送:
发送的数据帧是可编程的.它包括一个开始位,5到8个数据位,一个可选的校验位和1到2个停止位,这些都在ULCONn寄存器中控制.发送器可以产生终止条件,可以在一个数据帧的发送时间内强制串口输出为逻辑0.在当前的传输数据发送完成后会发送一个终止信号,在终止信号发送后,它会继续发送数据到Tx FIFO(而在非FIFO模式下是Tx holding寄存器).
数据接收:
和发送一样,接收的数据帧也是可编程的.它包括一个开始位,5到8个数据位,一个可选的校验位和1到2个停止位(ULCONn中设置).接收器可以探测overrun错误\parity错误\frame错误和终止条件,每一个都会设置一个错误标记.
overrun错误:在旧的数据被读完之前新的数据覆盖了旧的数据
parity错误:接收器探测到一个未知的校验条件
frame错误:接收数据没有有效的停止位
终止条件:RxDn输入在长于一个帧传输时间的期间内保持为逻辑0
中断/DMA请求产生
每个UART有7个状态信号(Tx/Rx/error):overrun error,parity error,frame error,break,接收缓冲数据准备,发送缓冲空和发送移位器空.所有都由对应的UART状态寄存器(UTRSTATn/UERSTATn)来指示.
overrun错误,parity错误,frame错误和终止条件都是接收错误状态.如果UCONn的接收错误状态中断使能位被置位,每一个错误都可以引起接收错误状态中断请求.当一个接收错误状态中断请求被探测到,可以通过读UERSTSTn来识别.
当在FIFO模式下接收器将数据从接收器的移位器发送到接收器的FIFO寄存器,接收的数据达到了Rx FIFO的触发线,Rx中断产生.如果在控制寄存器(UCONn)接收模式被选为1(中断请求或轮循模式).在非FIFO模式下,将数据从接收器的移位器发送到接收holding寄存器会引起Rx中断(中断请求和轮循模式下).
当发送器从发送器的FIFO寄存器发送数据到移位寄存器时,发送器的FIFO中存留的数据数量小于Tx FIFO的触发线时,Tx中断产生,如果控制寄存器的发送模式被选为中断请求或者轮循模式.在非FIFO模式下,在中断请求和轮循模式下从发送器的Holding寄存器发送数据到发送器的移位器会引起Tx中断.
如果控制寄存器的接收模式和发送模式选为DMA请求模式,在上面提到的条件下DMA请求会取代Tx或者Rx中断请求.
UART错误状态FIFO
UART除了有Rx FIFO寄存器外还有错误状态FIFO.错误状态FIFO会指明在FIFO寄存器中哪个数据在接收时是错误的.当有错的数据被读出时,错误中断就产生.为了清除错误状态FIFO,有错误的URXHn和UERSTATn就必须被读出.
波特率的产生:
每个UART的波特率产生器为发送器和接收器提供特定的时钟.波特率产生器的时钟源可以选为内部系统时钟或者UEXTCLK.换句话说,被除数由UCONn寄存器的时钟选择确定.波特率的时钟由源时钟(PCLK\FCLK/n\UEXTCLK)除以16和UART波特率除数寄存器(UBRDIVn)中的16bit除数.
公式如下:
UBRDIVn = (int)( UART clock / ( buad rate x 16) ) –1
( UART clock: PCLK, FCLK/n or UEXTCLK )
接下来看各个寄存器的描述:
UART LINE CONTROL REGISTER
ULCONn用来配置每一帧数据的格式的:
根据上图,配置如下:
IR mode[6] = 0(普通模式) Parity Mode[5:3] = 0xx(无校验) Number of Stop Bit[2] = 0(一个停止位)
word Length[1:0] = 11(8-bits)
UART CONTROL REGISTER
如果Uart的时钟源用FCLK/n,那么FCLK Divider就是配置这个n的
这个地方就是配置Uart的时钟源
Tx Interrupt Type [9] 中断请求类型:0是Pulse 1是Level
Rx Interrupt Type [8] 中断请求类型:0是Pulse 1是Level
Rx Time Out Enable [7] 在FIFO使能的情况下,是否打开Rx Time Out中断 0是关闭 1是打开
Rx Error Status Interrupt Enable [6] 使能Uart对异常产生中断,比如各种错误 0是关闭 1是打开
Loopback Mode [5] 回环模式 0是普通模式 1是回环模式
Send Break Signal [4] 是否发送终止信号 0是正常 1是发送终止信号
Transmit Mode [3:2] 决定使用哪种功能来写数据到发送缓冲寄存器 01中断请求或轮循模式
Receive Mode [3:2] 决定使用哪种功能来从接收缓冲寄存器读数据 01中断请求或轮循模式
UART FIFO CONTROL REGISTER
UART MODEM CONTROL REGISTER
这个和MODEM控制相关,设置为默认值就行.
UART TX/RX STATUS REGISTER
这个寄存器是只读的,负责反映发送接收的状态
UART ERROR STATUS REGISTER
这个寄存器也是只读的,负责反映错误状态
UART FIFO STATUS REGISTER
这个寄存器也是只读的,负责反映FIFO的状态
UART MODEM STATUS REGISTER
这个寄存器是MODEM状态的,不用管它
UART TRANSMIT BUFFER REGISTER (HOLDING REGISTER & FIFO REGISTER)
发送缓冲寄存器:FIFO模式下叫FIFO寄存器,非FIFO模式下叫Holding寄存器
需要注意的就是大小端
UART RECEIVE BUFFER REGISTER (HOLDING REGISTER & FIFO REGISTER)
接收缓冲寄存器:FIFO模式下叫FIFO寄存器,非FIFO模式下叫Holding寄存器
UART BAUD RATE DIVISOR REGISTER
波特率除数寄存器
UBRDIVn = (int)( UART clock / ( buad rate x 16) ) –1
( UART clock: PCLK, FCLK/n or UEXTCLK )
看完datasheet后,我们看一下TQ2440的原理图:
由原理图可以看出,TQ2440使用的UART0,我们用MAX232芯片做电平转换.
有了datasheet和原理图,下面就开始写汇编代码了:
时钟初始化和uart初始化用汇编写的:
- @.extern uart_test
- .equ GPBCON, 0x56000010
- .equ GPBDAT, 0x56000014
- .equ GPB5_out, (1<<(5*2))
- .equ GPB6_out, (1<<(6*2))
- .equ GPB7_out, (1<<(7*2))
- .equ GPB8_out, (1<<(8*2))
- .equ GPHCON, 0x56000070
- .equ GPHDAT, 0x56000074
- .equ GPBVALUE, (GPB5_out | GPB6_out | GPB7_out | GPB8_out)
- .equ LOCKTIME, 0x4c000000
- .equ MPLLCON, 0x4c000004
- .equ UPLLCON, 0x4c000008
- .equ M_MDIV, 92
- .equ M_PDIV, 1
- .equ M_SDIV, 1
- .equ U_MDIV, 56
- .equ U_PDIV, 2
- .equ U_SDIV, 2
- .equ CLKDIVN, 0x4c000014
- .equ DIVN_UPLL, 0
- .equ HDIVN, 1
- .equ PDIVN, 1 @FCLK : HCLK : PCLK = 1:2:4
- .equ ULCON0, 0x50000000
- .equ IR_MODE, 0x0 @[6]正常模式
- .equ Parity_Mode, 0x0 @[5:3]无校验位
- .equ Num_of_stop_bit, 0x0 @[2]一个停止位
- .equ Word_length, 0b11 @[1:0]8个数据位
- .equ UCON0, 0x50000004
- .equ FCLK_Div, 0 @[15:12] 时钟源选择用PCLK,所以这里用默认值
- .equ Clk_select, 0b00 @[11:10] 时钟源选择使用PCLK
- .equ Tx_Int_Type, 1 @[9] 中断请求类型为Level
- .equ Rx_Int_Type, 0 @1 @[8] 中断请求类型为Level
- .equ Rx_Timeout, 0 @[7]
- .equ Rx_Error_Stat_Int, 1 @[6]
- .equ Loopback_Mode, 0 @[5] 正常模式
- .equ Break_Sig, 0 @[4] 不发送终止信号
- .equ Tx_Mode, 0b01 @[3:2] 中断请求或轮循模式
- .equ Rx_Mode, 0b01 @[1:0] 中断请求或轮循模式
- .equ UFCON0, 0x50000008
- .equ Tx_FIFO_Trig_Level, 0b00 @[7:6]
- .equ Rx_FIFO_Trig_Level, 0b00 @[5:4]
- .equ Tx_FIFO_Reset, 0b0 @[2]
- .equ Rx_FIFO_Reset, 0b0 @[1]
- .equ FIFO_Enable, 0b0 @[0] 非FIFO模式
- .equ UMCON0, 0x5000000C @这个寄存器可以不管的
- .equ UTRSTAT0, 0x50000010
- .equ UERSTAT0, 0x50000014
- .equ UFSTAT0, 0x50000018
- .equ UMSTAT0, 0x5000001C
- .equ UTXH0, 0x50000020 @(L 小端)
- .equ URXH0, 0x50000024 @(L 小端)
- .equ UBRDIV0, 0x50000028
- .equ UBRDIV, 0x35 @PCLK=400M/4=100M UBRDIV = (int)(100M/115200/16) - 1 = 53 = 0x35
- .global _main
- _main:
- ldr r0,=GPBCON
- ldr r1,=0x15400
- str r1, [r0]
- ldr r2,=GPBDAT
- bl clock_setup
- bl uart_init
- bl delay
- ldr lr, =loop
- @bl uart_test dd
- ldr pc, _uart_test
- _uart_test: .word uart_test
- loop:
- b loop @ 死循环
- ledloop:
- ldr r1,=0x1c0
- str r1,[r2]
- bl delay
- ldr r1,=0x1a0
- str r1,[r2]
- bl delay
- ldr r1,=0x160
- str r1,[r2]
- bl delay
- ldr r1,=0x0e0
- str r1,[r2]
- bl delay
- b ledloop
- clock_setup:
- ldr r0,=LOCKTIME
- ldr r1,=0xffffffff
- str r1, [r0]
- ldr r0,=CLKDIVN
- ldr r1,=(DIVN_UPLL<<3) | (HDIVN<<1) | (PDIVN<<0)
- str r1, [r0]
- ldr r0,=UPLLCON
- ldr r1,=(U_MDIV<<12) | (U_PDIV<<4) | (U_SDIV<<0) @Fin=12M UPLL=48M
- str r1, [r0]
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- ldr r0,=MPLLCON
- ldr r1,=(M_MDIV<<12) | (M_PDIV<<4) | (M_SDIV<<0) @Fin=12M FCLK=400M
- str r1, [r0]
- mov pc,lr
- uart_init:
- ldr r0,=GPHCON
- ldr r1,=0x2aaaa @配置GPIO复用规则为串口
- str r1, [r0]
- ldr r0,=ULCON0
- ldr r1,=(IR_MODE<<6) | (Parity_Mode<<3) | (Num_of_stop_bit<<2) | (Word_length<<0) @
- str r1, [r0]
- ldr r0,=UCON0
- ldr r1,=(FCLK_Div<<12) | (Clk_select<<10) | (Tx_Int_Type<<9) | (Rx_Int_Type<<8) | (Rx_Timeout<<7) | (Rx_Error_Stat_Int<<6) |(Loopback_Mode<<5) | (Break_Sig<<4) | (Tx_Mode<<2) | (Rx_Mode<<0)
- str r1, [r0]
- ldr r0,=UFCON0
- ldr r1,=(Tx_FIFO_Trig_Level<<6) | (Rx_FIFO_Trig_Level<<4) | (Tx_FIFO_Reset<<2) | (Rx_FIFO_Reset<<1) | (FIFO_Enable<<0) @
- str r1, [r0]
- ldr r0,=UBRDIV0
- ldr r1,=(UBRDIV<<0)
- str r1, [r0]
- mov pc,lr
- delay:
- @ldr r3,=0x4ffffff
- ldr r3,=0xfffff
- delay1:
- sub r3,r3,#1
- cmp r3,#0x0
- bne delay1
- mov pc,lr
UART的测试代码用C写的:
uart_test.c:
- #include "uart_test.h"
- char uart_GetByte(void)
- {
- while(!(rUTRSTAT0 & 0x1)); //Wait until THR is empty.
- return RdURXH0();
- }
- void uart_GetString(char *pt)
- {
- while(*pt)
- uart_GetByte();
- }
- void uart_SendByte(int data)
- {
- if(data=='\n')
- {
- while(!(rUTRSTAT0 & 0x2));
- WrUTXH0('\r');
- }
- while(!(rUTRSTAT0 & 0x2)); //Wait until THR is empty.
- WrUTXH0(data);
- }
- //====================================================================
- void uart_SendString(char *pt)
- {
- while(*pt)
- uart_SendByte(*pt++);
- }
- void uart_test(void)
- {
- char str[20] = "\nhello world\n";
- int a = 97;
- //while(1)
- // uart_SendByte(a);
- uart_SendString(str);
- char s = uart_GetByte();
- //if(s == 'a')
- if(s == 97)
- rGPBDAT = 0x1c0;
- //uart_SendByte(a);
- //uart_SendByte(97);
- //uart_SendByte('a');
- uart_SendByte((int)s);
- uart_SendByte((int)'s');
- }
uart_test.h:
- //#define WrUTXH0(ch) (*(volatile unsigned char *)0x50000023)=(unsigned char)(ch)
- #define WrUTXH0(ch) (*(volatile unsigned char *)0x50000020)=(unsigned char)(ch)
- #define RdURXH0() (*(volatile unsigned char *)0x50000024)
- #define rUTRSTAT0 (*(volatile unsigned *)0x50000010) //UART 0 Tx/Rx status
- #define rGPBDAT (*(volatile unsigned *)0x56000014)
到此,使用串口的轮循模式,可以实现串口的正常收发功能了~~
中断模式还没写,这个等到后面全面研究uboot的时候再回头看,uboot用哪种模式,应该就说明那种方式好,到时侯再实现吧
转载于:blog.csdn.net/dndxhej