转自:
版权声明:本文为博主原创文章,未经博主允许不得转载。
1.相关原理图
2.相关寄存器介绍与配置
1)与管脚相关
①GPHCON
注:
配置成UART0功能:
GPHCON |= 10 10 10 10b(还要在GPHUP里把管脚禁止上拉)
2)与中断相关
上传图片好麻烦,跟以前相关的这里就不贴了
3)与UART0相关
①ULCON0
注:
Infrared Mode :选择普通模式
Parity Mode : 选择不执行奇偶校验
Number of stop :选择1个结束位
Word length :8-bits字长
ULCON0 = 0x03
②UBRDIV0和UCON0
注:
UBRDIVn = (int)(UART clock /(baud rate * 16)) - 1,因为下面我们选择了PCLK作为UARTclock。所以UBRDIV0=(int)PCLK/(baudrate*16)) - 1。其中PCLK=101.25MHz,波特率选择115200
UBRDIV0=(int)( 101250000/(115200*16) ) - 1 = 54.
选择UBRDIV0= 0x36
注:
Clock selection :时钟源选择,选择PCLK,根据上个定时器实验得知PCLK使用101.25MHz。00b
注:
脉冲触发:指的是在Tx buffer从有数据跳变为空时(对于接收模式来说,则是Rx buffer从空跳变为有数据)触发一次边沿中断,之后即使为空,但是没有这个跳变是不会触发中断的(Non-FIFO)。对于使用FIFO来说,一旦到达Tx/Rx FIFO的触发水平时才产生一个边沿中断,其它时候都不会触发中断。
水平触发:指的是只要Tx buffer为空(对于接收模式来说,则是只要Rx buffer有数据)则一直产生中断请求 (电平中断)。对于使用FIFO来说,只要是高于(接收模式)/低于(发送模式)或等于触发水平就一直产生中断(电平中断)。
①如果发送中断采用了脉冲触发,在发送中断处理里执行太长时间的情况下,导致下个脉冲中断请求在中断处理函数里产生了,那么如果请求中断请求位是在后面清除的,则会引起下个脉冲中断请求被清除了而无法再次进入发送中断(因为数据已经被发送出去了,则不会再达到触发水平(FIFO)或者从从有数据到空的跳变(non-FIFO))。而如果是采用水平触发则不会发生这种情况,因为只要是空的(non-FIFO)或低于等于触发水平(FIFO),即便被清除了请求位,还是会产生请求,但是这样会连绵不断的触发中断。
②如果接收中断采用了脉冲触发,在接收中断处理里执行太长时间的情况下,也会导致下一个脉冲中断请求在中断处理函数里产生了,那么如果请求中断请求位是在后面清除的,则会引起下个脉冲中断请求被清除了而无法再次进入发送中断(因为之前的数据还没被取出,则不会再达到触发水平(FIFO)或者从空到有数据的跳变(non-FIFO))。而如果是采用水平触发则不会发生这种情况,因为只要有数据(non-FIFO)或高于等于触发水平(FIFO),即便被清除了请求位,还是会产生请求,但是这样会连绵不断的触发中断。
举个栗子:
①只使能发送中断,选择FIFO,触发水平为empty,在发送中断里发送一个字节,延迟3秒,这段时间足够让Tx FIFO里从一个数据跳变到empty(2),即发送中断请求(1)在中断处理函数里产生了。
·使用脉冲触发:退出中断处理函数清除了中断请求,也就把(1)给清掉了,进不了发送中断无法给数据,并且Tx FIFO一直为空(无法出现(2)) , 因此不再产生中断。
·使用水平触发:退出中断处理函数清除了中断请求,也把(1)给清掉了,但是只要是低于或等于empty在FIFO里,则电平中断还会产生,能够再次进入中断。
②只使能接收中断,选择FIFO,触发水平为1-byte,PC机上发一个字节触发中断,在接收中断里接收一个字节,延迟3秒,在这段时间里再发1个字节,这样会有一个从empty到1-byte的跳变(2),这样就在中断服务函数里产生了中断请求,即接收中断请求(1)在中断处理函数里产生了。
·使用脉冲触发:退出中断处理函数清除了中断请求,也就把(1)给清掉了,进不了接收中断无法取出数据,并且Rx FIFO一直1-byte数据(无法出现(2)) ,因此不再产生中断。
·使用水平触发:退出中断处理函数清除了中断请求,也把(1)给清掉了,但是只要是高于或等于1-byte数据在FIFO里,则电平中断还会产生,能够再次进入中断。
经测试成立。
Tx interrupt type :保险点选择水平触发就行了,不过这个我们只在需要发送数据再打开,发送完数据再关闭,这样就防止老是进入中断。1b
Rx interrupt type :同理 ,选择水平触发就好了,只要有数据就进入中断处理数据。1b
Rx timeout enable :选择禁止。0b
Rx error status interrupt enable :禁止接收错误状态中断。0b
Loopback mode :选择普通模式。0b
Send break signal :选择正常传输。0b
注:
Transmit mode :选择中断请求或者轮询模式。01b
Receive mode :选择中断模式或者轮询模式。01b
UCON0 = 0x305
③UFCON0
注:
Tx FIFO trigger level :选择emply,只有在FIFO到达空了才会触发中断。00b
Rx FIFO trigger level :选择1-bype,这样只要接收到1个字节就直接进入中断取出处理。00b
Tx FIFO reset :选择normal。0b
Rx FIFO reset :选择normal。0b
FIFO enable :使能FIFO。1b
UFCON0 = 0x01
④UMCON0
注:
AFC flow control:不使能自动流控。0b
Request to send:不使能自动流控,这个位也不需要用到
初始化时:UMCON0 = 0;
⑤UTRSTAT0
注:只读
Transmitter empty:当发送缓冲寄存器(FIFO)已经没有有效数据可以发送并且发送数据移位器为空。该域会被置为‘1’。
Transmit buffser empty:当发送缓冲寄存器空时被置为‘1’。(只针对于Non-FIFO,Tx FIFO模式需要检测UFSTAT寄存器的count 位和Tx FIFO Full 位)
Receive buffer data ready:当接收缓冲寄存器包含了从PXDn端口接收到的有效数据时被设置为‘1’。(只针对于Non-FIFO,Rx FIFO模式需要检测UFSTAT寄存器的count位和Rx FIFO Full 位)
⑥UERSTAT0
注:(当这个状态寄存器被读取时会自动清零)
Break detect:当一个break 信号被接收到了这个位会被置‘1’。
Frame Error :当一个帧错误在接收操作期间产生时被置‘1’。
Parity error :当一个奇偶校验错误在接收期间产生时被置‘1’。
Overrun error:当一个overrun错误在接收期间产生时被置‘1’。
⑦UFSTAT0
注:
Tx FIFO full :当发送FIFO在发送期间满了,这个位会被置‘1’。
Tx FIFO count :Tx FIFO里的data数。
Rx FIFO Full :当接收FIFO在接收期间满了,这个位会被置‘1’。
Rx FIFO count :Rx FIFO里的data数。
⑧UMSTAT0
注:
Delta CTS:指明了nCTS输入到S3C2440A状态是否改变了知道该位被CPU读取了才自动清零。
Clear to send:要发送数据时需要检查该位是否有效,有效才可以写数据到Tx FIFO。
这两个都用在自动流控功能里。如果双方都不使用自动流控。这个也可以不管。
⑨UTXH0(UART0发送缓冲寄存器)
注:
TXDATA0:存放用户要发送的数据
⑩URXH0(UART0接收缓冲寄存器)
注:
(当overrun error发生时,URXH0必须被读出,否则,下一个接收到的数据也会造成overrun error尽管UERSTAT0的overrun位已经被清除了)
RXDATA:存放接收到的数据
3.流程图设计
①主流程图
②中断处理子程序
③中断服务子函数
4.程序设计
①Makefile
uart0.bin : head.o demoUart0.o
arm-linux-ld -Tuart0.lds -o uart0_elf $^ arm-linux-objcopy -O binary -S uart0_elf $@ arm-linux-objdump -D -m arm uart0_elf > uart0.dis%.o : %.S arm-linux-gcc -Wall -c -o $@ $<%.o : %.c arm-linux-gcc -Wall -c -o $@ $<clean: rm -f uart0.dis uart0.bin uart0_elf *.o *.bak/****************************************************************************************************************************/
②uart0.lds
SECTIONS {
first 0x00000000 : { head.o } second 0x30000000 : AT(2048) { demoUart0.o }}/*****************************************************************************************************************************/
③head.S
@与内存相关
.equ BWSCON , 0x48000000.equ BANKCON6 , 0x4800001C.equ REFRESH , 0x48000024.equ BANKSIZE , 0x48000028.equ MRSRB6 , 0x4800002C@与中断相关.equ INTMSK , 0x4A000008.equ INTSUBMSK , 0x4A00001C.equ SUBSRCPND , 0x4A000018.equ SRCPND , 0x4A000000.equ INTPND , 0x4A000010@与看门狗相关.equ WTCON , 0x53000000@与管脚配置相关的.equ GPHCON , 0x56000070.equ GPHUP , 0x56000078@与UART0配置相关的.equ ULCON0 , 0x50000000.equ UBRDIV0 , 0x50000028.equ UCON0 , 0x50000004.equ UFCON0 , 0x50000008.equ UMCON0 , 0x5000000C.equ UTRSTAT0 , 0x50000010 @read-only.equ UERSTAT0 , 0x50000014.equ UFSTAT0 , 0x50000018.equ UMSTAT0 , 0x5000001C.equ UTXH0 , 0x50000020 @little-endian.equ URXH0 , 0x50000024 @little-endian@时钟相关寄存器.equ MPLLCON , 0x4C000004.equ UPLLCON , 0x4C000008.equ CLKDIVN , 0x4C000014.equ CAMDIVN , 0x4C000018@与灯光配置相关的.equ GPBCON , 0x56000010.equ GPBDAT , 0x56000014.text.global _start_start:/***********设置中断向量表*************/ b ResetInit @复位异常入口HandlerUndef: b HandlerUndef @未定义异常入口HandlerSWI: b HandlerSWI @软中断异常入口HandlerPabort: b HandlerPabort @取指中止异常入口HandlerDabort: b HandlerDabort @数据中止异常入口HandlerNotUsed: b HandlerNotUsed @保留 b HandlerIRQ @中断异常入口HandlerFIQ: b HandlerFIQ @快中断异常入口/************END设置中断向量表***********/ResetInit:/*************关闭看门狗****************/ ldr r0 , =WTCON mov r1 , #0x0 str r1 , [r0]/************END关闭看门狗**************//**********初始化LED灯管脚************ @把LED1-4管脚置为输出 ldr r0 , =GPBCON ldr r1 , [r0] @把GPBCON里的内容加载到r1里 ldr r2 , =(0xFF<<10) bic r1 , r1 ,r2 @操作数取反码或上r1,用于清零工作 ldr r2 , =(0x55<<10) orr r1 , r1 , r2 str r1 , [r0] ldr r0 , =GPBDAT ldr r1 , [r0] ldr r2 , =(0x0F<<5) bic r1 , r1 , r2 orr r1 , r1 , r2 str r1 , [r0]***********END***************/ /**********初始化相关管脚为UART0功能************/ ldr r0 , =GPHCON ldr r1 , [r0] @把GPHCON里的内容加载到r1里 ldr r2 , =0xFF bic r1 , r1 ,r2 @操作数取反码&r1,用于清零 ldr r2 , =0xAA orr r1 , r1 , r2 str r1 , [r0] ldr r0 , =GPHUP ldr r1 , [r0] bic r1 , r1 , #0x0f orr r1 , r1 , #0x0f str r1 , [r0] @管脚禁止上拉功能/***********END***************/ /***********配置时钟相关寄存器***********/ ldr lr , =Memconf ldr pc , =ClkConfigure/**********END配置时钟相关寄存器*********/ /***********配置内存相关寄存器***********/Memconf: ldr lr , =InitSystemSp ldr pc , =MemConfigure/**********END配置内存相关寄存器*********//***********设置系统模式下的sp***********/InitSystemSp: @复位默认进入系统模式 ldr sp , =0x34000000/********END设置系统模式下的sp***********//**********配置UART0相关寄存器***********/ ldr lr , =CopyToSdram ldr pc , =Uart0Configure/*******************END******************//************拷贝中断处理子函数到内存****/CopyToSdram: ldr lr , =Uart0IntConf ldr pc , =copy_bootsram_to_sdram/*******************END******************//********配置UART0中断相关寄存器*******/Uart0IntConf: ldr r0 , =INTMSK @使能UART0中断 ldr r1 , [r0] bic r1 , r1 , #(0x01<<28) str r1 , [r0] ldr r0 , =INTSUBMSK @禁止INT_ERR0(1<<2), ldr r1 , [r0] @使能INT_TXD0(0<<1),INT_RXD0(0<<0),中断 bic r1 , r1 , #0x07 orr r1 , r1 , #0x06 str r1 , [r0]/******END配置UART0中断相关寄存器******//*进入中断模式设置中断模式下的sp退出到系统模式*使能IRQ中断*/ msr cpsr_c , 0xd2 @进入中断模式,禁止中断,其中cpsr后的_c表示cpsr[7:0] ldr sp , =0x33500000 @设置sp msr cpsr_c , 0x5f @退出到系统模式,使能IRQ中断/*END进入中断模式设置中断模式下的sp退出到系统模式*//*************等待中断******************/halt_loop: b halt_loop/*********************END****************/ /*************IRQ中断服务子程序**********/HandlerIRQ: @因为在产生中断异常时,lr存的是当前指令的下一条指令,所以要减四 sub lr , lr , #4 @把相关寄存器压入中断模式下的栈 @db表示sp每次传送内容前减1 stmdb sp! , {r0-r12 , lr} @禁止IRQ中断 mrs r1 , cpsr_all orr r1 , r1 , #(1<<7) msr cpsr_all , r1 @调用中断服务处理函数 ldr lr , =IRQ_Return ldr pc , =main IRQ_Return:/* ldr r0 , =GPBDAT ldr r1 , [r0] ldr r2 , =(0x0F<<5) bic r1 , r1 , r2 orr r1 , r1 , r2 str r1 , [r0]*/ @把栈里面的内容推出到相应寄存器里,并把lr推到pc寄存器实现跳转 @ia表示每次传送后加1 , 当寄存器列表中包含了pc寄存器,选用^为后缀,就会把spsr拷贝到cpsr ldmia sp! , {r0-r12 , pc}^ /********END IRQ中断服务子程序***********//*配置Uart0相关寄存器*/Uart0Configure: ldr r0 , =ULCON0 mov r1 , #0x03 str r1 , [r0] @普通模式,禁止奇偶校验,1个结束位,8-bit字长 ldr r0 , =UBRDIV0 mov r1 , #0x36 str r1 , [r0] @波特率选择115200,所以UBRDIV0=54 ldr r0 , =UCON0 ldr r1 , =0x305 str r1 , [r0] @时钟源=PCLK @Rx,Tx水平触发,禁止接收超时中断,禁止接收错误状态中断, @不使用回路模式,不发送break信号 @中断方式发送接收数据到缓冲寄存器 ldr r0 , =UFCON0 mov r1 ,#0x01 str r1 , [r0] @Tx FIFO触发水平选择空时触发, @Rx FIFO触发水平选择>=1字节触发 @使能FIFO ldr r0 , =UMCON0 mov r1 , #0x0 str r1 , [r0] @禁止自动流控功能。 bx lr /******************END******************//***********配置时钟相关寄存器***********/ClkConfigure: @UPLL选择MDIV=0x38,PDIV=2,SDIV=1.得UPLL clock=96MHz @这里先设置UPLL是为了与设置MPLL隔开至少7个NOP的时间间隔 ldr r0 , =UPLLCON ldr r1 , =(0x38<<12)|(0x02<<4)|(0x01<<0) str r1 , [r0] @分频比FCLK:HCLK:PCLK=1:4:4 , UCLK=UPLL_clock/2=48MHz ldr r0 , =CLKDIVN mov r1 , #(0x01<<3)|(0x02<<1)|(0x00<<0) str r1 , [r0] ldr r0 , =CAMDIVN ldr r1 , [r0] bic r1 , r1 , #(0x03<<8) str r1 , [r0] @因为HDIVN不是0 , 所以CPU总线模式要从高速总线模式改变 @为异步总线模式 mrc p15 , 0 , r1 , c1 , c0 , 0 orr r0 , r0 , #0xC0000000 mcr p15 , 0 , r0 , c1 , c0 , 0 @MPLL选择MDIV=0x7f,PDIV=2,SDIV=1,得MPLL clock=405MHz ldr r0 , =MPLLCON ldr r1 , =(0x7f<<12)|(0x02<<4)|(0x01<<0) str r1 , [r0] bx lr/***************END*************************//*******内存初始化子程序*********/MemConfigure: @BWSCON[27:24] = 0 0 10B ldr r0 , =BWSCON ldr r1 , [r0] ldr r2 , =(0x0F<<24) bic r1 , r1 , r2 ldr r2 , =(0x02<<24) orr r1 , r1 , r2 str r1 , [r0] @BANKCON6[16:15]=11B,BANKCON6[3:0]=00 01B ldr r0 , =BANKCON6 ldr r1 , [r0] ldr r2 , =(0x03<<15) bic r1 , r1 , r2 orr r1 , r1 , r2 ldr r2 , =0x0F bic r1 , r1 , r2 ldr r2 , = 0x01 orr r1 , r1 , r2 str r1 , [r0] @这里的Trp要大于20ns , Trc要大于70ns,HCLK=101.25MHz @故时钟周期=1s/HCLK=9.8ns,Trp*9.8>20ns ==> Trp>=3 ==> Trp域=01b @Trp*9.8+Tsrc*9.8>70ns ==> Tsrc>=5 ==> Tsrc域=01b @REFRESH[23:18] = 1 0 01 01B,REFRESH[10:0] = 0x4E8 ldr r0 , =REFRESH ldr r1 , [r0] ldr r2 , =(0x3F<<18) bic r1 , r1 , r2 ldr r2 , =(0x25<<18) orr r1 , r1 , r2 ldr r2 , =0x7FF bic r1 , r1 , r2 ldr r2 , =0x4E9 orr r1 , r1 , r2 str r1 , [r0] @BANKSIZE[7:0] = 1 0 1 1 0 001 B ldr r0 , =BANKSIZE ldr r1 , [r0] ldr r2 , =0xFF bic r1 , r1 , r2 ldr r2 , =0xB1 orr r1 , r1 , r2 str r1 , [r0] @MRSRB6[11:0] = 0 00 011 0 000 B ldr r0 , =MRSRB6 ldr r1 , [r0] ldr r2 , =0x3FF bic r1 , r1 , r2 ldr r2 , =0x030 orr r1 , r1 , r2 str r1 , [r0] bx lr @函数返回/******END内存初始化子程序*******//******拷贝后2048到4096之间的代码到sdram*******/copy_bootsram_to_sdram: ldr r0 , =0x800 ldr r1 , =0x30000000 ldr r2 , =0x1000copy: ldr r3 , [r0] , #4 str r3 , [r1] , #4 cmp r0 , r2 bne copy bx lr/*****END拷贝前4K代码到sdram*****//****************************************************************************************************************/④demoUart0.h
#ifndef DEMOUART0_H#define DEMOUART0_H#define GPBCON (*(volatile unsigned long *)0x56000010)#define GPBDAT (*(volatile unsigned long *)0x56000014)#define INTOFFSET (*(volatile unsigned long *)0x4A000014)#define SRCPND (*(volatile unsigned long *)0x4A000000)#define INTPND (*(volatile unsigned long *)0x4A000010)#define SUBSRCPND (*(volatile unsigned long *)0x4A000018)#define INTSUBMSK (*(volatile unsigned long *)0x4A00001C)#define UTXH0 (*(volatile unsigned long *)0x50000020)#define URXH0 (*(volatile unsigned long *)0x50000024)#define UFSTAT0 (*(volatile unsigned long *)0x50000018)#define UMCON0 (*(volatile unsigned long *)0x5000000C)#define INT_TXD0 (0x01<<1)#define INT_RXD0 (0x01)#define TFIFO_FULL (0x01<<14) #define RFIFO_DATA_NUM (0x1f)/*消息队列大小*/#define QUEUE_SIZE 100unsigned char p[QUEUE_SIZE];/**选择了水平触发所以这里可以直接把所以中断请求清除,*退出之后只要满足触发条件还是会触发中断**/#define cleanIQR() \ do {\ SUBSRCPND = SUBSRCPND;\ SRCPND = SRCPND;\ INTPND = INTPND;\ } while(0) /*接收数据的消息队列结构体*/ typedef struct MSG_QUEUE { unsigned char *Qmsg; unsigned char *write; unsigned char *read; unsigned char size;} MSG_QUEUE;/***************函数声明********************//*把消息队列qmsg里的消息通过uart0发送出去*/void uart0_sent_msg(MSG_QUEUE *qmsg);/*把接收到的消息存入到qmsg消息队列里*/void uart0_revice_msg(MSG_QUEUE *qmsg);/*延时函数*/void delay(volatile unsigned long second);/*******************************************//*********************函数定义************************/void delay(volatile unsigned long second){ volatile unsigned long i; while(second--){ i=1000000; while(--i); } }void uart0_sent_msg(MSG_QUEUE *qmsg){ while( !(UFSTAT0&TFIFO_FULL) && qmsg->size ) { /*如果Tx FIFO还被满,并且队列还有数据要发送,则一直循环填入UTXH0*/ UTXH0 = *qmsg->read++; --qmsg->size; if(qmsg->read == (qmsg->Qmsg+QUEUE_SIZE-1) ){ qmsg->read = qmsg->Qmsg; } } return ; }void uart0_revice_msg(MSG_QUEUE *qmsg){ while( (UFSTAT0 & RFIFO_DATA_NUM) && (qmsg->size < QUEUE_SIZE) ){ /*如果接收FIFO里还有数据并且消息队列uart0_qmsg里数据没满,则继续取出数据*/ *qmsg->write++ = (volatile unsigned char)URXH0; ++qmsg->size; if(qmsg->write == (qmsg->Qmsg+QUEUE_SIZE-1) ) { qmsg->write = qmsg->Qmsg; } } return;}/******************************************/#endif/***********************************************************************************************************************/
⑤demoUart0.c
#include "demoUart0.h"
int main(){ static unsigned char i; static MSG_QUEUE *uart0_qmsg; if( INTOFFSET != 28 ){ cleanIQR(); return 0; } /*第一次进来初始化队列*/ if(i==0){ uart0_qmsg->Qmsg = p; uart0_qmsg->write = p; uart0_qmsg->read = p; uart0_qmsg->size=0; i = 1; } /*接收中断处理*/ if(SUBSRCPND&INT_RXD0){ uart0_revice_msg(uart0_qmsg); /*如果消息队列里有数据,则使能INT_TXD0中断*/ if(uart0_qmsg->size){ INTSUBMSK &= ~(1<<1); } cleanIQR(); return 0; } /*发送中断处理*/ if(SUBSRCPND&INT_TXD0){ uart0_sent_msg(uart0_qmsg); /*关闭INT_TXD0中断使能*/ INTSUBMSK |= (1<<1); cleanIQR(); return 0; } /*选择了水平触发所以这里可以直接把所以中断请求清除,退出之后只要满足触发条件还是会触发中断*/ cleanIQR(); return 0;}