韦东山嵌入式第一期学习笔记DAY

it2022-05-05  190

作者:GWD 时间:2019.7.18

课程内容:在interrupt中注册中断,用C实现类似面向对象的函数指针结构体——构造类似面向对象中类的概念,所谓类就是有变量,有方法(函数)。 一、思路 第一步:在interrupt.c中定义一个函数数组; 注: 1、这种构造函数数组的方法适用于,程序需要相同类型但型号不同的外设,但是不想每次添加新的型号都在底层文件中修改设备名称的情况,比如LCD屏幕、中断函数等,都是同一类的参数类型返回值均相同,但是具体的功能函数却不一样,可以合并为一“类”; 2、函数数组的实现手法是先定义一种新的类型的函数指针,然后用这种新的类型定义一个数组; 第二步: 1、在数组中需要选取哪个功能函数执行是根据typedef void(*irq_func)(int)中,参数Int确定的,所以接下来要写一个注册函数,注册函数的输入参数应该有两个,一个是函数在数组中的标号,另一个是功能函数的首地址(C中函数首地址就是函数的名称)赋值给函数数组。 第三步: 1、接下来就要在每个新加入的外设中(本节课讲的是中断源)的初始化函数中调用底层提供的注册函数了。注册新的硬件,这样的好处是,“自力更生,自己事自己办”,只需要在代码中添加新加入硬件的初始化和功能函数就行了,不需要修改底层代码了。 第四步: 1、注册完之后该出手时就要出手——解引用,引用时直接调用数组中的某个函数就行了,因为需要用到的外设(本课讲得是中断号)已经通过注册函数注册进数组里了; 2、注意解引用的时候不要忘记给“函数指针传递参数” 第五步: 1、在main.c中调用初始化函数。 二、代码

INTERRUPT.C

#include "s3c2440_soc.h" typedef void(*irq_func)(int); irq_func irq_array[32]; /* SRCPND 用来显示哪个中断产生了, 需要清除对应位 * bit0-eint0 * bit2-eint2 * bit5-eint8_23 */ /* INTMSK 用来屏蔽中断, 1-masked * bit0-eint0 * bit2-eint2 * bit5-eint8_23 */ /* INTPND 用来显示当前优先级最高的、正在发生的中断, 需要清除对应位 * bit0-eint0 * bit2-eint2 * bit5-eint8_23 */ /* INTOFFSET : 用来显示INTPND中哪一位被设置为1 */ /* 初始化中断控制器 */ void register_irq(int irq,irq_func fp) { irq_array[irq] = fp; INTMSK &= ~(1 << irq); } /* 初始化按键, 设为中断源 */ void key_eint_init(void) { /* 配置GPIO为中断引脚 */ GPFCON &= ~((3<<0) | (3<<4)); GPFCON |= ((2<<0) | (2<<4)); /* S2,S3被配置为中断引脚 */ GPGCON &= ~((3<<6) | (3<<22)); GPGCON |= ((2<<6) | (2<<22)); /* S4,S5被配置为中断引脚 */ /* 设置中断触发方式: 双边沿触发 */ EXTINT0 |= (7<<0) | (7<<8); /* S2,S3 */ EXTINT1 |= (7<<12); /* S4 */ EXTINT2 |= (7<<12); /* S5 */ /* 设置EINTMASK使能eint11,19 */ EINTMASK &= ~((1<<11) | (1<<19)); register_irq(0,key_eint_irq); register_irq(2,key_eint_irq); register_irq(5,key_eint_irq); } /* 读EINTPEND分辨率哪个EINT产生(eint4~23) * 清除中断时, 写EINTPEND的相应位 */ void key_eint_irq(int irq) { unsigned int val = EINTPEND; unsigned int val1 = GPFDAT; unsigned int val2 = GPGDAT; if (irq == 0) /* eint0 : s2 控制 D12 */ { if (val1 & (1<<0)) /* s2 --> gpf6 */ { /* 松开 */ GPFDAT |= (1<<6); } else { /* 按下 */ GPFDAT &= ~(1<<6); } } else if (irq == 2) /* eint2 : s3 控制 D11 */ { if (val1 & (1<<2)) /* s3 --> gpf5 */ { /* 松开 */ GPFDAT |= (1<<5); } else { /* 按下 */ GPFDAT &= ~(1<<5); } } else if (irq == 5) /* eint8_23, eint11--s4 控制 D10, eint19---s5 控制所有LED */ { if (val & (1<<11)) /* eint11 */ { if (val2 & (1<<3)) /* s4 --> gpf4 */ { /* 松开 */ GPFDAT |= (1<<4); } else { /* 按下 */ GPFDAT &= ~(1<<4); } } else if (val & (1<<19)) /* eint19 */ { if (val2 & (1<<11)) { /* 松开 */ /* 熄灭所有LED */ GPFDAT |= ((1<<4) | (1<<5) | (1<<6)); } else { /* 按下: 点亮所有LED */ GPFDAT &= ~((1<<4) | (1<<5) | (1<<6)); } } } EINTPEND = val; } void handle_irq_c(void) { /* 分辨中断源 */ int bit = INTOFFSET; /* 调用对应的处理函数 */ irq_array[bit](bit); /* 清中断 : 从源头开始清 */ SRCPND = (1<<bit); INTPND = (1<<bit); }

Timer.c

#include "s3c2440_soc.h" void timer_init(void) { /* 设置TIMER0的时钟 */ /* Timer clk = PCLK / {prescaler value+1} / {divider value} = 50000000/(99+1)/16 = 31250 */ TCFG0 = 99; /* Prescaler 0 = 99, 用于timer0,1 */ TCFG1 &= ~0xf; TCFG1 |= 3; /* MUX0 : 1/16 */ /* 设置TIMER0的初值 */ TCNTB0 = 15625; /* 0.5s中断一次 */ /* 加载初值, 启动timer0 */ TCON |= (1<<1); /* Update from TCNTB0 & TCMPB0 */ /* 设置为自动加载并启动 */ TCON &= ~(1<<1); TCON |= (1<<0) | (1<<3); /* bit0: start, bit3: auto reload */ /* 设置中断 */ register_irq(10, timer_irq); } void timer_irq(void) { /* 点灯计数 */ static int cnt = 0; int tmp; cnt++; tmp = ~cnt; tmp &= 7; GPFDAT &= ~(7<<4); GPFDAT |= (tmp<<4); }

最新回复(0)