手机版
你好,游客 登录 注册
背景:
阅读新闻

GNU ARM汇编

s3c2440的串口控制

[日期:2012-08-12] 来源:Linux社区  作者:dndxhej [字体: ]

在配置完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初始化用汇编写的:

  1. @.extern    uart_test  
  2.   
  3. .equ    GPBCON, 0x56000010    
  4. .equ    GPBDAT,  0x56000014    
  5.   
  6. .equ GPB5_out,  (1<<(5*2))    
  7. .equ GPB6_out,  (1<<(6*2))    
  8. .equ GPB7_out,  (1<<(7*2))    
  9. .equ GPB8_out,  (1<<(8*2))    
  10.   
  11. .equ    GPHCON, 0x56000070  
  12. .equ    GPHDAT, 0x56000074  
  13.         
  14. .equ GPBVALUE,    (GPB5_out | GPB6_out | GPB7_out | GPB8_out)    
  15.   
  16. .equ    LOCKTIME, 0x4c000000  
  17. .equ    MPLLCON, 0x4c000004  
  18. .equ    UPLLCON, 0x4c000008  
  19. .equ    M_MDIV, 92  
  20. .equ   M_PDIV, 1  
  21. .equ    M_SDIV, 1  
  22. .equ    U_MDIV, 56  
  23. .equ   U_PDIV, 2  
  24. .equ    U_SDIV, 2  
  25.   
  26. .equ    CLKDIVN, 0x4c000014  
  27. .equ    DIVN_UPLL, 0  
  28. .equ    HDIVN,  1  
  29. .equ    PDIVN,  1    @FCLK : HCLK : PCLK = 1:2:4  
  30.   
  31.   
  32. .equ    ULCON0, 0x50000000  
  33. .equ    IR_MODE,    0x0   @[6]正常模式  
  34. .equ    Parity_Mode,    0x0 @[5:3]无校验位  
  35. .equ    Num_of_stop_bit,  0x0  @[2]一个停止位  
  36. .equ    Word_length,    0b11    @[1:0]8个数据位  
  37.   
  38. .equ    UCON0,  0x50000004  
  39. .equ    FCLK_Div,   0   @[15:12]  时钟源选择用PCLK,所以这里用默认值  
  40. .equ    Clk_select, 0b00    @[11:10] 时钟源选择使用PCLK  
  41. .equ    Tx_Int_Type, 1  @[9]  中断请求类型为Level  
  42. .equ    Rx_Int_Type, 0 @1  @[8]  中断请求类型为Level  
  43. .equ    Rx_Timeout, 0  @[7]  
  44. .equ    Rx_Error_Stat_Int, 1 @[6]  
  45. .equ    Loopback_Mode, 0 @[5]  正常模式  
  46. .equ    Break_Sig,  0 @[4] 不发送终止信号  
  47. .equ    Tx_Mode,    0b01 @[3:2]  中断请求或轮循模式  
  48. .equ    Rx_Mode,    0b01 @[1:0]  中断请求或轮循模式  
  49.   
  50. .equ    UFCON0, 0x50000008  
  51. .equ    Tx_FIFO_Trig_Level, 0b00 @[7:6]  
  52. .equ    Rx_FIFO_Trig_Level, 0b00 @[5:4]  
  53. .equ    Tx_FIFO_Reset,  0b0 @[2]  
  54. .equ    Rx_FIFO_Reset,  0b0 @[1]  
  55. .equ    FIFO_Enable,    0b0 @[0] 非FIFO模式  
  56.   
  57. .equ    UMCON0, 0x5000000C    @这个寄存器可以不管的  
  58. .equ    UTRSTAT0,   0x50000010  
  59. .equ    UERSTAT0,   0x50000014  
  60. .equ    UFSTAT0,    0x50000018  
  61. .equ    UMSTAT0,    0x5000001C  
  62. .equ    UTXH0,      0x50000020   @(L 小端)  
  63. .equ    URXH0,      0x50000024   @(L 小端)  
  64.   
  65. .equ     UBRDIV0,    0x50000028  
  66. .equ     UBRDIV,    0x35   @PCLK=400M/4=100M   UBRDIV = (int)(100M/115200/16) - 1 = 53 = 0x35  
  67.   
  68.   
  69. .global _main    
  70. _main:   
  71.   
  72.     ldr r0,=GPBCON  
  73.   
  74.     ldr r1,=0x15400  
  75.     str r1, [r0]  
  76.   
  77.     ldr r2,=GPBDAT  
  78.   
  79.     bl clock_setup  
  80.         bl uart_init  
  81.     bl delay  
  82.   
  83.   
  84.        ldr lr, =loop                              
  85.        @bl  uart_test  dd               
  86.     ldr pc, _uart_test  
  87.   
  88. _uart_test: .word uart_test  
  89. loop:  
  90.         b loop                                               @ 死循环  
  91.   
  92. ledloop:  
  93.   
  94.     ldr r1,=0x1c0  
  95.     str r1,[r2]  
  96.     bl delay  
  97.   
  98.     ldr r1,=0x1a0  
  99.     str r1,[r2]  
  100.     bl delay  
  101.   
  102.     ldr r1,=0x160  
  103.     str r1,[r2]  
  104.     bl delay  
  105.   
  106.     ldr r1,=0x0e0  
  107.     str r1,[r2]  
  108.     bl delay  
  109.   
  110.   
  111.     b ledloop  
  112.   
  113. clock_setup:  
  114.   
  115.     ldr r0,=LOCKTIME  
  116.     ldr r1,=0xffffffff  
  117.     str r1, [r0]  
  118.   
  119.     ldr r0,=CLKDIVN  
  120.     ldr r1,=(DIVN_UPLL<<3) | (HDIVN<<1) | (PDIVN<<0)  
  121.     str r1, [r0]  
  122.   
  123.     ldr r0,=UPLLCON  
  124.     ldr r1,=(U_MDIV<<12) | (U_PDIV<<4) | (U_SDIV<<0)   @Fin=12M  UPLL=48M  
  125.     str r1, [r0]  
  126.     nop  
  127.     nop  
  128.     nop  
  129.     nop  
  130.     nop  
  131.     nop  
  132.     nop  
  133.     ldr r0,=MPLLCON  
  134.     ldr r1,=(M_MDIV<<12) | (M_PDIV<<4) | (M_SDIV<<0)    @Fin=12M  FCLK=400M  
  135.     str r1, [r0]  
  136.   
  137.   
  138.   
  139.     mov pc,lr  
  140.   
  141. uart_init:  
  142.     ldr r0,=GPHCON  
  143.     ldr r1,=0x2aaaa     @配置GPIO复用规则为串口  
  144.     str r1, [r0]  
  145.   
  146.     ldr r0,=ULCON0  
  147.     ldr r1,=(IR_MODE<<6) | (Parity_Mode<<3) | (Num_of_stop_bit<<2) | (Word_length<<0)    @  
  148.     str r1, [r0]  
  149.   
  150.     ldr r0,=UCON0  
  151.     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)  
  152.     str r1, [r0]  
  153.   
  154.     ldr r0,=UFCON0  
  155.     ldr r1,=(Tx_FIFO_Trig_Level<<6) | (Rx_FIFO_Trig_Level<<4) | (Tx_FIFO_Reset<<2) | (Rx_FIFO_Reset<<1) | (FIFO_Enable<<0)    @  
  156.     str r1, [r0]  
  157.   
  158.     ldr r0,=UBRDIV0  
  159.     ldr r1,=(UBRDIV<<0)  
  160.     str r1, [r0]  
  161.   
  162.         mov pc,lr  
  163.   
  164. delay:  
  165.     @ldr r3,=0x4ffffff  
  166.     ldr r3,=0xfffff  
  167.   
  168. delay1:  
  169.     sub r3,r3,#1  
  170.   
  171.     cmp r3,#0x0  
  172.   
  173.     bne delay1  
  174.   
  175.     mov pc,lr  

UART的测试代码用C写的:

uart_test.c:

  1. #include "uart_test.h"   
  2.   
  3. char uart_GetByte(void)  
  4. {  
  5.         while(!(rUTRSTAT0 & 0x1));   //Wait until THR is empty.   
  6.             return RdURXH0();  
  7. }  
  8.   
  9.   
  10. void uart_GetString(char *pt)  
  11. {  
  12.     while(*pt)  
  13.         uart_GetByte();  
  14. }  
  15.   
  16.   
  17. void uart_SendByte(int data)  
  18. {  
  19.       
  20.         if(data=='\n')  
  21.         {  
  22.             while(!(rUTRSTAT0 & 0x2));  
  23.             WrUTXH0('\r');  
  24.         }  
  25.         while(!(rUTRSTAT0 & 0x2));   //Wait until THR is empty.   
  26.         WrUTXH0(data);  
  27.   
  28. }                 
  29.   
  30. //====================================================================   
  31. void uart_SendString(char *pt)  
  32. {  
  33.     while(*pt)  
  34.            uart_SendByte(*pt++);  
  35. }  
  36.   
  37.   
  38. void uart_test(void)  
  39. {  
  40.       
  41.     char str[20] = "\nhello world\n";  
  42.     int a = 97;  
  43.     //while(1)   
  44.     //  uart_SendByte(a);      
  45.     uart_SendString(str);  
  46.     char s = uart_GetByte();  
  47.     //if(s == 'a')   
  48.     if(s == 97)  
  49.         rGPBDAT = 0x1c0;  
  50.     //uart_SendByte(a);   
  51.     //uart_SendByte(97);   
  52.     //uart_SendByte('a');   
  53.     uart_SendByte((int)s);  
  54.     uart_SendByte((int)'s');  
  55. }  

uart_test.h:

  1. //#define WrUTXH0(ch) (*(volatile unsigned char *)0x50000023)=(unsigned char)(ch)   
  2. #define WrUTXH0(ch) (*(volatile unsigned char *)0x50000020)=(unsigned char)(ch)   
  3. #define RdURXH0() (*(volatile unsigned char *)0x50000024)   
  4. #define rUTRSTAT0   (*(volatile unsigned *)0x50000010)  //UART 0 Tx/Rx status   
  5.   
  6. #define    rGPBDAT   (*(volatile unsigned *)0x56000014)    

到此,使用串口的轮循模式,可以实现串口的正常收发功能了~~

中断模式还没写,这个等到后面全面研究uboot的时候再回头看,uboot用哪种模式,应该就说明那种方式好,到时侯再实现吧。

 

 

 

linux
【内容导航】
第1页:开篇 第2页:汇编编译链接与运行
第3页:ARM处理器的基本原则 第4页:中断汇编之非嵌套中断处理
第5页:中断汇编之嵌套中断处理 第6页:s3c2440的时钟控制
第7页:s3c2440的串口控制 第8页:s3c2440的watchdog
第9页:s3c2440的PWM 第10页:s3c2440的RTC
第11页:小结 第12页:arm汇编指令的B真的那么简单吗?
第13页:GNU ARM汇编下的linker script 第14页:GNU ARM汇编下做任务调度
第15页:Linux下的printascii 第16页:bootloader与kernel之间的commandline的传递
第17页:u-boot的makefile和mkconfig解读 第18页:u-boot-采用nand_spl方式的启动方法
第19页:u-boot-nand-spl启动过程分析 第20页:总结
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款