UART使用中断方式的流程:
1.组件有:DDR上面的一个buffer,UART上的FIFO,CPU
2.拿发送来说,UART上面FIFO可以设置一个阈值,当UART上的FIFO小于这个阈值的时候,UART就会发送一个中断给CPU,CPU就会将DDR上buffer中的数据自动发送给UART上的FIFO直到满为止,当FIFO又小于这个阈值的时候,又会进行这个操作。
如下图所示:
在上图中用到的buffer是一个环形缓冲区:
环形缓冲区相当与一个数组,比如:char buff[10],它分为下面几个情形(R:读指针,W:写指针):
1.环形缓冲区为空:R == W 都只想buff[0]
2.放入数据: buff[w] = val;w = (w+1)%10
3.取出数据 : val = buff[R] ; R = (R+1)%10 注:这个10是根据具体的环形缓冲区大小为定的。
4.环形缓冲区为满 : (w +1)%10 == R (这样的写法是为了和空的时候进行区别)
中断处理完成后,不仅要清中断源的中断,还要清中断控制器的中断:
列出部分代码:
uart初始化以及发送接受
#define ULCON0 (*((volatile unsigned long *)0x7F005000))
#define UCON0 (*((volatile unsigned long *)0x7F005004))
#define UFCON0 (*((volatile unsigned long *)0x7F005008))
#define UMCON0 (*((volatile unsigned long *)0x7F00500C))
#define UTRSTAT0 (*((volatile unsigned long *)0x7F005010))
#define UFSTAT0 (*((volatile unsigned long *)0x7F005018))
#define UTXH0 (*((volatile unsigned char *)0x7F005020))
#define URXH0 (*((volatile unsigned char *)0x7F005024))
#define UBRDIV0 (*((volatile unsigned short *)0x7F005028))
#define UDIVSLOT0 (*((volatile unsigned short *)0x7F00502C))
#define UINTP0 (*((volatile unsigned long *)0x7F005030))
#define UINTM0 (*((volatile unsigned long *)0x7F005038)) //uart 中断mask寄存器
#define GPACON (*((volatile unsigned long *)0x7F008000))
#define ENABLE_FIFO 1
static delay(void)
{
volatile int i = 10;
while (i--);
}
void uart_tx_int_enable(void) //使能uart中断源
{
UINTM0 &= ~(1<<2);
}
void uart_tx_int_disable(void) //禁止uart中断源
{
UINTM0 |= (1<<2);
}
void init_uart(void) //初始化uart
{
GPACON &= ~0xff; //设置uart引脚为中断引脚,只有中断方式才会使用到
GPACON |= 0x22;
/* ULCON0 */
ULCON0 = 0x3; /* 数据位:8, 无较验, 停止位: 1, 8n1 */
UCON0 = 0x5 | (1<<9); /* 使能UART发送、接收, tx interrupt request type = level 使用电平触发*/
#ifdef ENABLE_FIFO
UFCON0 = 0x07 | (1<<6); /* FIFO enable, tx fifo trigger level = 16 bytes 设置触发阈值*/
#else
UFCON0 = 0x00; /* FIFO disable */
#endif
UMCON0 = 0;
/* 波特率 */
/* DIV_VAL = (PCLK / (bps x 16 ) ) - 1
* bps = 57600
* DIV_VAL = (66500000 / (115200 x 16 ) ) - 1
* = 35.08
*/
UBRDIV0 = 35;
/* x/16 = 0.08
* x = 1
*/
UDIVSLOT0 = 0x1;
UINTM0 = 0xF; //初始化的时候先关闭中断源
}
unsigned char getc(void) //读取一个字符
{
#ifdef ENABLE_FIFO
while ((UFSTAT0 & (1<<6)) == 0 && (UFSTAT0 & 0x3f) == 0)delay();
#else
while ((UTRSTAT0 & (1<<0)) == 0);
#endif
return URXH0;
}
int getc_nowait(unsigned char *pChar) //非阻塞获取一个字符
{
#ifdef ENABLE_FIFO
if ((UFSTAT0 & (1<<6)) == 0 && (UFSTAT0 & 0x3f) == 0)
#else
if ((UTRSTAT0 & (1<<0)) == 0)
#endif
{
return -1;
}
else
{
*pChar = URXH0;
return 0;
}
}
#define TX_BUF_LEN 2048
static unsigned char txbuf[2047];
static unsigned int r_idx = 0;
static unsigned int w_idx = 0;
static int isFull(void) //判断环形缓冲区是否已经满
{
if ((w_idx + 1) % TX_BUF_LEN == r_idx)
return 1;
else
return 0;
}
static int isEmpty(void) //判断环形缓冲区是不是空
{
return (w_idx == r_idx);
}
static int putData(unsigned char data) //在环形缓冲区中放入数据
{
if (isFull())
return -1;
else
{
txbuf[w_idx] = data;
w_idx = (w_idx + 1) % TX_BUF_LEN;
return 0;
}
}
static int getData(unsigned char *pdata) //从环形缓冲区获取数据
{
if (isEmpty())
{
return -1;
}
else
{
*pdata = txbuf[r_idx];
r_idx = (r_idx + 1) % TX_BUF_LEN;
return 0;
}
}
void putc(char c)
{
putData(c); /* 把数据放到缓冲区里去 */
/* 如果"uart 发送中断"未使能的话,使能"uart 发送中断" */
uart_tx_int_enable();
}
void do_uart_irq(void) //uart的中断处理
{
int i;
int cnt;
unsigned char c;
if (UINTP0 & (1<<2))
{
/* 对于发送中断 */
if (isEmpty())
{
/* 禁止中断 */
uart_tx_int_disable();
}
else
{
/* 从环型缓冲区里取出数据, 放到TX FIFO里去 */
cnt = (UFSTAT0 >> 8) & 0x3f;
cnt = 64 - cnt;
for (i = 0; i < cnt; i++)
{
if (getData(&c) == 0)
{
UTXH0 = c;
}
else
{
break;
}
}
}
}
else if (UINTP0 & (1<<0))
{
/* 对于接收中断, 从RX FIFO里取出数据 */
}
/* 清中断 */
UINTP0 = 0xf;
}
irq中断控制器的设置:
#define GPNCON (*((volatile unsigned long *)0x7F008830))
#define GPNDAT (*((volatile unsigned long *)0x7F008834))
#define EINT0CON0 (*((volatile unsigned long *)0x7F008900))
#define EINT0MASK (*((volatile unsigned long *)0x7F008920))
#define EINT0PEND (*((volatile unsigned long *)0x7F008924))
#define PRIORITY (*((volatile unsigned long *)0x7F008280))
#define SERVICE (*((volatile unsigned long *)0x7F008284))
#define SERVICEPEND (*((volatile unsigned long *)0x7F008288))
#define VIC0IRQSTATUS (*((volatile unsigned long *)0x71200000))
#define VIC0FIQSTATUS (*((volatile unsigned long *)0x71200004))
#define VIC0RAWINTR (*((volatile unsigned long *)0x71200008))
#define VIC0INTSELECT (*((volatile unsigned long *)0x7120000c))
#define VIC0INTENABLE (*((volatile unsigned long *)0x71200010))
#define VIC0INTENCLEAR (*((volatile unsigned long *)0x71200014))
#define VIC0PROTECTION (*((volatile unsigned long *)0x71200020))
#define VIC0SWPRIORITYMASK (*((volatile unsigned long *)0x71200024))
#define VIC0PRIORITYDAISY (*((volatile unsigned long *)0x71200028))
#define VIC0VECTADDR0 (*((volatile unsigned long *)0x71200100))
#define VIC0VECTADDR1 (*((volatile unsigned long *)0x71200104))
#define VIC0ADDRESS (*((volatile unsigned long *)0x71200f00))
#define VIC1IRQSTATUS (*((volatile unsigned long *)0x71300000))
#define VIC1VECTADDR5 (*((volatile unsigned long *)0x71300114))
#define VIC1INTENABLE (*((volatile unsigned long *)0x71300010))
#define VIC1ADDRESS (*((volatile unsigned long *)0x71300f00))
void uart_irq_init(void);
void key_irq_init(void);
void eint0_3_irq(void) //按键中断的处理
{
int i;
printf("eint0_3_irq\n\r"); /* K1~K4 */
for (i = 0; i < 4; i ++)
{
if (EINT0PEND & (1<<i))
{
if (GPNDAT & (1<<i))
{
printf("K%d released\n\r", i+1);
}
else
{
printf("K%d pressed\n\r", i+1);
}
}
}
}
void eint4_11_irq(void)
{
int i;
printf("eint4_11_irq\n\r"); /* K5~K6 */
for (i = 4; i < 6; i ++)
{
if (EINT0PEND & (1<<i))
{
if (GPNDAT & (1<<i))
{
printf("K%d released\n\r", i+1);
}
else
{
printf("K%d pressed\n\r", i+1);
}
}
}
}
void irq_init(void) //中断初始化:按键和uart的中断初始化
{
key_irq_init();
uart_irq_init();
}
void uart_irq(void)
{
/* 调用具体的中断处理函数 */
do_uart_irq(); //处理中断,清中断源的中断
/* 清中断 */
VIC1ADDRESS = 0; //清中断控制器的中断
}
void uart_irq_init(void)
{
VIC1INTENABLE |= (1<<5); /* bit5: int_uart0 */ 使能中断控制器组的开关
VIC1VECTADDR5 = uart_irq;//uart中断处理,会自动赋值给VIC1ADDRESS
}
void key_irq_init(void)
{
/* 配置GPIO引脚为中断引脚 */
/* GPN0~5 设为中断引脚 */
GPNCON &= ~(0xfff);
GPNCON |= 0xaaa;
/* 设置中断触发方式为: 双边沿触发 */
EINT0CON0 &= ~(0xfff);
EINT0CON0 |= 0x777;
/* 使能中断 */
EINT0MASK &= ~(0x3f);
/* 在中断控制器里使能这些中断 */
VIC0INTENABLE |= (0x3); /* bit0: eint0~3, bit1: eint4~11 */
VIC0VECTADDR0 = eint0_3_irq;
VIC0VECTADDR1 = eint4_11_irq;
/* 设置优先级 */
}
void do_irq(void)
{
int i = 0;
void (*the_isr)(void);
if (VIC0IRQSTATUS) //判断是哪个中断控制器发生的中断
{
the_isr = VIC0ADDRESS;
/* 2.1 分辨是哪个中断 */
/* 2.2 调用它的处理函数 */
/* 2.3 清中断 */
the_isr();
EINT0PEND = 0x3f; /* 清中断 */
VIC0ADDRESS = 0;
}
else if (VIC1IRQSTATUS)
{
the_isr = VIC1ADDRESS;
/* 2.1 分辨是哪个中断 */
/* 2.2 调用它的处理函数 */
/* 2.3 清中断 */
the_isr();
}
}