韦东山ARM裸机和uboot大全(第1期加强版)学习笔记18-第018课_ADC和触摸屏_第006~008节_触摸屏编程_ADC中断

在上一节代码基础上继续编写

一 006节-触摸屏编程_ADC中断(把XY坐标读取出来)

本节目的:检测到触摸屏按下的时候需要启动ADC中断读取xy位置坐标。

//相对于上一节代码本函数修改了ADCDLY寄存器值
//因为之前配置值过小,触摸屏的电压输出尚未稳定,所以需要增大自按下触摸笔到产生触摸屏中断的时间
void adc_ts_reg_init(void)
{
    
    
	...
	/*  按下触摸屏, 延时一会再发出TC中断
	 *  延时时间 = ADCDLY * 晶振周期 = ADCDLY * 1 / 12000000 = 5ms
	 */
	ADCDLY = 60000;	
}

//中断服务程序
void AdcTsIntHandle(int irq)
{
    
    
	if (SUBSRCPND & (1<<TC_INT_BIT))  /* 如果是触摸屏中断 */
		Isr_Tc();

	if (SUBSRCPND & (1<<ADC_INT_BIT))  /* ADC中断 */
		Isr_Adc();
	SUBSRCPND = (1<<TC_INT_BIT) | (1<<ADC_INT_BIT);
}
//***********************************
//中断服务程序调用子函数1
void Isr_Adc(void)
{
    
    
	int x = ADCDAT0;//x方向位置坐标
	int y = ADCDAT1;//y方向位置坐标

	if (!(x & (1<<15))) /* 如果仍然按下才打印 */
	{
    
    
		x &= 0x3ff;
		y &= 0x3ff;
		
		printf("x = %08d, y = %08d\n\r", x, y);
	}
	//此时已经按下,需要等待触摸笔松开
	enter_wait_pen_up_mode();
}

//***********************************
//中断服务程序调用子函数2
void Isr_Tc(void)
{
    
    
	if (ADCDAT0 & (1<<15))
	{
    
    
		//printf("pen up\n\r");
		enter_wait_pen_down_mode();
	}
	else	
	{
    
    
		//printf("pen down\n\r");

		/* 进入"自动测量"模式 */
		enter_auto_measure_mode();

		/* 启动ADC */
		ADCCON |= (1<<0);//开始ADC转换
	}
}
/* 进入"自动测量"模式 */
/* ADCTSC's bits */
#define WAIT_PEN_DOWN    (0<<8)
#define WAIT_PEN_UP      (1<<8)

#define YM_ENABLE        (1<<7)
#define YM_DISABLE       (0<<7)

#define YP_ENABLE        (0<<6)
#define YP_DISABLE       (1<<6)

#define XM_ENABLE        (1<<5)
#define XM_DISABLE       (0<<5)

#define XP_ENABLE        (0<<4)
#define XP_DISABLE       (1<<4)

#define PULLUP_ENABLE    (0<<3)
#define PULLUP_DISABLE   (1<<3)

#define AUTO_PST         (1<<2)//自动顺序转换xy坐标

#define WAIT_INT_MODE    (0b11)//等待中断
#define NO_OPR_MODE      (0)

void enter_auto_measure_mode(void)
{
    
    
	ADCTSC = AUTO_PST | NO_OPR_MODE;
}


二 007节-触摸屏编程_定时器中断

  1. 上一节程序编码思路:触摸屏按下的时刻 -> 触摸屏按下中断 -> 配置开始ADC测量中断 -> 等待触摸屏松开
  2. 缺点:如果触摸屏一直按下就一直卡死在等待触摸屏松开,不会一直驱动adc进行数据转换。
  3. 解决:添加定时器中断,若触摸屏被按下,定时启动ADC转换和转换中断。

1 定时器中断函数改进

注意:这部分裸机代码设计思路之前没有接触过,这里仔细学习一下
首先,科普一下函数指针的用法:函数指针是指向函数的指针变量,他的本质是一个指针变量。

int (*f)  (int x); /* 声明一个函数指针 */
f=func; /* 将func函数的首地址赋给指针f */

其次,定义一个函数指针类型:

typedef void(*variable)();/*声明一个void(*)() 类型的函数指针variable。*/

参见typedef void(*Func)(void) 用法

//本函数是注册的定时器中断服务程序
void timer_irq(void)
{
    
    
	int i;
	//这里比较新鲜:作者计划按照调用几个函数进行执行,使用了函数数组的形式
	//TIMER_NUM:最大能够调用函数数量,这里是一个宏
	for (i = 0; i < TIMER_NUM; i++)
	{
    
    
		//timer_array是一个结构体数组,fp是一个函数指针
		//这里依次定义结构体数组中的每一个函数指针对应的函数
		if (timer_array[i].fp)//只要指针非空,则对应的函数被调用;否则,继续调用下一个
		{
    
    
			timer_array[i].fp();
		}
	}	
}

//定义一个函数指针类型
typedef void(*timer_func)(void);
//定义一个结构体类型
typedef struct timer_desc {
    
    
	char *name;
	timer_func fp;
}timer_desc;
//定义一个结构体数组
timer_desc timer_array[TIMER_NUM];

//在结构体数组中添加某个函数
//name:可以理解为函数代号;fp:等待加入的函数指针
int register_timer(char *name, timer_func fp)
{
    
    
	int i;
	for (i = 0; i < TIMER_NUM; i++)
	{
    
    
		//如果这个位置的函数为空,则添加进来;
		//否则,继续向后遍历。
		if (!timer_array[i].fp)
		{
    
    
			timer_array[i].name = name;
			timer_array[i].fp   = fp;
			return 0;
		}
	}
	return -1;
}

//在结构体数组中去掉某个函数
//name:可以理解为函数代号;
void unregister_timer(char *name)
{
    
    
	int i;
	//遍历结构体数组
	for (i = 0; i < TIMER_NUM; i++)
	{
    
    
		//如果遍历到了一个数组中成员的名字与输入参数一致,
		//把对应的成员参数置为空!
		if (!strcmp(timer_array[i].name, name))
		{
    
    
			timer_array[i].name = NULL;
			timer_array[i].fp   = NULL;
			return 0;
		}
	}
	return -1;
}

猜你喜欢

转载自blog.csdn.net/xiaoaojianghu09/article/details/104399908