使用fifo的好处有:
1:串口的数据发送的数据量较大时,使用fifo可以大大降低MCU的开销。
(有点类似串入并出的cput处理模型,本质上还是串行收发)
2:在某些特殊场合,例如制定较复杂的协议时,可以使用fifo特性来做协议简化,比如一包
数据包含8个字节,(并且fifo设置的长度为8),这样相当于把uart转换为类似CAN/以太网模型,
这样信息可扩展性得到了质的提高,当然,这里需要同步协调。
fifo分析拓展:
1. 如果要用中断来处理接收到的数据,就是说,接收完数据然后产生中断,再于中断里处理接收的数据。如果要实现这个本意,要设置好触发点。
至于超时中断之类,那是另外一回事了。
2. 就UART的中断类型而言,还包括:RBR中断使能、THRE中断使能、RX线状态中断使能、自动波特率超时中断使能等。
3.
FIFO定义说了,先入先出缓冲区,在UART中有什么用呢。我们可以用它来批量发送 数据(而非FIFO模式,只能一字节一字节的发,而且发每个字节都需要等待缓冲区变空才能发送下一字节),这样能在一定程度上提高数据发送速度。还有就是 避免数据包的的丢失(这个问题,目前还没体会)。
需要了解的相关知识:
HCLK主要为 S3C2440 AHB 总线( Advanced High performance Bus )上挂接硬件提供工作频率, AHB 总线主要挂接有内存, NAND , LCD 控制器等硬件
FCLK 主要为 ARM920T 内核提供工作频率;
PCLK 主要为 APB 总线提供工作频率, APB 总线主要挂接 UART 串口, Watchdog 等硬件控制器。 【所以在这里我们用PCLK作为UART的时钟源】
外接设备 |
起始地址 |
结束地址 |
存储控制器 |
0x48000000 |
0x48000030 |
USB Host控制器 |
0x49000000 |
0x49000058 |
中断控制器 |
0x4A000000 |
0x4A00001C |
DMA |
0x4B000000 |
0x4B0000E0 |
时钟和电源管理 |
0x4C000000 |
0x4C000014 |
LCD控制器 |
0x4D000000 |
0x4D000060 |
NAND FLASH控制器 |
0x4E000000 |
0x4E000014 |
摄像头接口 |
0x4F000000 |
0x4F0000A0 |
UART |
0x50000000 |
0x50008028 |
脉宽调制计时器 |
0x51000000 |
0x51000040 |
USB设备 |
0x52000140 |
0x5200026F |
WATCHDOG计时器 |
0x53000000 |
0x53000008 |
IIC控制器 |
0x54000000 |
0x5400000C |
IIS控制器 |
0x55000000 |
0x55000012 |
I/O端口 |
0x56000000 |
0x560000B0 |
实时时钟RTC |
0x57000040 |
0x5700008B |
A/D转换器 |
0x58000000 |
0x58000010 |
SPI |
0x59000000 |
0x59000034 |
SD接口 |
0x5A000000 |
0x5A000040 |
AC97音频编码接口 |
0x5B000000 |
0x5B00001C
|
在start.s中完成对看门狗,内存等进行初始化:
GPBCON
EQU 0x56000010
GPBDAT EQU 0x56000014
AREA Init,CODE,READONLY
//声明一个只读性质的代码段Init.
ENTRY
//入口
start
close watchdog
ldr r0,=0x53000000;
//将看门狗控制寄存器地址放入r0
mov r1,#0
str r1,[r0];
//将r1的值0放到r0中,即设置看门/
狗控制寄存器的值为0
bl initmem;
//跳转到initmem代码段,初始化内存
IMPORT main;
//引入main.c中的main函数
ldr sp,=0x34000000;
//调用c程序前先初始化桟指针
ldr lr,=loop;
//设置main函数的返回地址
ldr pc,=main;
//跳转到c程序的main函数的入口处执行
loop
b loop ;
//死循环
initmen
//内存初始化
ldr r0,=0x48000000;
//加载内存相关寄存器首地址r0
ldr r1,=0x48000034;
//加载内存相关寄存器尾地址到r1
adr r2,memdata;
//将寄存器配置数据地址段首地址加载到r2
initmemloop
ldr r3,[r2],#4;
//循环设置存寄存器
str r3,[r0],#4
teq r0,r1
bne initmemloop;
//循环到最后一个寄存器时退出函数
mov pc,lr
memdata
// 存放内存控制器设置数据
DCD 0x22000000 ;BWSCON
DCD 0x00000700 ;BANKCON0
DCD 0x00000700 ;BANKCON1
DCD 0x00000700 ;BANKCON2
DCD 0x00000700 ;BANKCON3
DCD 0x00000700 ;BANKCON4
DCD 0x00000700 ;BANKCON5
DCD 0x00018005 ;BANKCON6
DCD 0x00018005 ;BANKCON7
DCD 0x008e07a3 ;REFRESH
DCD 0x000000b1 ;BANKSIZE
DCD 0x00000030 ;MRSRB6
DCD 0x00000030 ;MRSRB7
END
在uart.c中:
#define GPHCON (*(volitile unsigned long *)0X56000070)
#define GPHDAT (*(volitile unsigned long *)0X56000074)
#define GPHUP (*(volitile unsigned long *)0X56000078)
#define UFCON0 (*(volitile unsigned long *)0x50000008)
#define UMCON0 (*(volitile unsigned long *)0X5000000C)
#define UCON0 (*(volitile unsigned long *)0X50000004)
#define ULCON0 (*(volitile unsigned long *)0X50000000)
#define UART_CLK PCKL
//UART0的时钟源为PCLK
#define UART_BAUD_DATE 115200
//波特率
#define UART_BRD ((UART_CLK/(UART_BAUD_DATE*16))-1)
void uart_init(void)
{
UFCON0=0xbf;
//使用fifo(发送fifo和接收fifo的触发深度都为32字节)
【在中断模式下的fifo要设置触发方式(超时触发或字节触发)】
UMCON0=0x00;
//不使用流控
UBRDIV0=UART_BRD;
//波特率为115200
GPHCON |=0xa0;
//GPH2,GPH3用作TXD0,RXD0
GPHUP=0x0c;
//GPH2,GPH3内部上拉
UCON0=0X05
//时钟源为PCKL
ULCON0 &=0X03;
//正常模式,数据格式:8个数据位,没有流控,1个数据位
}
当发送缓冲区里有数据需要传送时,我们就需要中断,来完成发送数据的任务。这个中断的产生,是由 s3c24xx_serial_start_tx()函数来完成的,具体来说,是它所调用的一个是中断使能的函数 enable_irq(TX_IRQ(port))来完成的。而此中断一旦使能,就会调用该中断的服务函数 s3c24xx_serial_tx_chars()去做后续的工作。
其次,在响应函数中,要注意检测TxFIFO是否已满。
发 送FIFO,是串口传送数据非常重要的机制。相当于提供了一个发送前的缓冲区,给我们的发送工作的多样化的处理带来了可能。我们的中断响应函数需要把数据 从发送缓冲区先搬送到TxFIFO中,但搬送之前需要做的工作就是判断FIFO是不是已经满了,如果已经满了,就不能再往里放数据了。
再次,在响应函数中,要注意检测传送缓冲区是否已空。
这一点也很重要,我们要发送的数据来源于数据缓冲区,如果该缓冲区的数据是空的,那我们就不需要去传送了。
最后,如何做到连续不断地传送数据。
我们从数据缓冲区里把数据传到TxFIFO中,要想持续地传送数据,很显然还需要有人往数据缓冲区里不断地放入新的数据。
void putc(unsigned char c)
{
while(UFSTAT0 & (UFSTAT0=0<<14))
{
UTXH0=C;
}
unsigined char getc(void)
{
while(UFSTAT0 &(UFSTAT0=0<<6)
)
{
return URXH0;
}
在main.c中:
#include "start.s"
#include "uart.h"
int main()
{
unsigned char c;
uart_init();
while(1)
{
c=getc();
if(isDigit(c)||isLetter(c))
putc(c);
return 0;
}
用FIFO发送数据时,若所用为轮询法(用在非高速场合,高速场合最好用BDMA或中 断法,故此轮询法很少用)需要保证接受FIFO不溢出,类似中断法,通过软件来设置触发条件。例如 if(rUFSTAT0&0x000f>0x0008)触发读为FIFO内有8字节数据。但是我们同时还要考虑,不能空读接收FIFO,例 如while((rUFSTAT0&0x000F)>0x0000){ 读接收FIFO}。这种轮询法比较山寨,稳定性不咋地,适用低速。不提倡使用。