韦东山嵌入式Linux学习——017 ADC和触摸屏(3)-长按实现

ADC和触摸屏(3)-长按实现

/*
 *硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3)
 *软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.04_x64 系统,
 *参考资料:开发版原理图,S3C2440A datasheet
*/


一、前言

  在上篇的博客中,实现了触摸笔的按下松开检测,那么在这篇中,则实现触摸笔的长按功能。

二、程序设计

1、定时器中断函数

  实现触摸笔的长按功能,需要使用到定时器中断,在之前的学习中,我们已经编写了timer.c文件,为了程序的拓展性,所以打算仿照内核中注册与卸载驱动的程序

  1. 定义函数指针,指向具体的定时器中断函数
typedef void(*timer_fp)(void);  		//定义函数指针
  1. 定义一个结构体数组,统一管理各中定时器中断函数
#define MAX_NUM 	(10)

typedef struct timer_desc{
	char *name;
	timer_fp fp;
}timer_desc, *p_timer_desc;

timer_desc timer_num[MAX_NUM];	//定义结构体数组,存储定时器中断函数和名字
  1. 编写注册卸载定时器中断函数
/* 定时器中断注册函数 */
int register_timer(char *name, timer_fp fp)
{
	int i;

	/* 遍历找到空项 */
	for (i = 0; i < MAX_NUM ; i++) {
		if (!timer_num[i].fp) {
			timer_num[i].name = name;
			timer_num[i].fp    = fp;
			return 0;
		}
	}
	return -1;	
}

/* 卸载函数 */
int unregister_timer(char *name)
{
	int i;

	/* 遍历找到对应项 */
	for (i = 0; i < MAX_NUM ; i++) {
		if (!strcmp(timer_num[i].name, name)) {
			timer_num[i].name = NULL;
			timer_num[i].fp    = NULL;
			return 0;
		}
	}
	return -1;	
}

三、程序编写

1、timer.c文件修改

#include "s3c2440_soc.h"

#define MAX_NUM 	(10)
#define NULL		((void *)0)

typedef void(*timer_fp)(void);  		//定义函数指针

typedef struct timer_desc{
	char *name;
	timer_fp fp;
}timer_desc, *p_timer_desc;

timer_desc timer_num[MAX_NUM];	//定义结构体数组,存储定时器中断函数和名字

/* 定时器中断注册函数 */
int register_timer(char *name, timer_fp fp)
{
	int i;

	/* 遍历找到空项 */
	for (i = 0; i < MAX_NUM ; i++) {
		if (!timer_num[i].fp) {
			timer_num[i].name = name;
			timer_num[i].fp    = fp;
			return 0;
		}
	}
	return -1;	
}

/* 卸载函数 */
int unregister_timer(char *name)
{
	int i;

	/* 遍历找到对应项 */
	for (i = 0; i < MAX_NUM ; i++) {
		if (!strcmp(timer_num[i].name, name)) {
			timer_num[i].name = NULL;
			timer_num[i].fp    = NULL;
			return 0;
		}
	}
	return -1;	
}

/* 定时器中断相关函数 */
void timer_irq(void)
{
	int i;
	
	/* 遍历找到非空项执行 */
	for (i = 0; i < MAX_NUM ; i++) {
		if (timer_num[i].name) {
			timer_num[i].fp();
		}
	}
}

void timer0_init(void)
{
	/* 
	 * 初始化时钟:Timer 0
	 * =PCLK / {prescaler value+1} / {divider value}
	 * 50000000/(49+1)/16=62500
	 */
	TCFG0 = 49;    		//设置prescaler value
	TCFG1 &=~ 0xf;   
	TCFG1 |= 0x0003;    //设置divider value

	/* 设置初值*/
	TCNTB0 = 625;  		//10ms中断一次
	
	/* 加载初值,启动时钟 */
	TCON = (1<<1);   	//手动更新,Update TCNTB0 & TCMPB0

	/* 设置为自动加载模式 */
	TCON &=~(1<<1);
	TCON |= (1<<0)|(1<<3);

	register_irq(10, timer_irq);	
}

2、touchscreen.c文件修改


/* 函数处理流程:
 *		1、在main函数中,处理好一系列的初始化工作
 *
 *		2、定时器中断程序每间隔10ms,
 *			2.1 根据g_ts_timer_enable的值设置不同的寄存器,已实现不同的处理
 *			2.2 根据LED处理函数,每定时中断50次,点亮LED灯,实现计数
 *
 *		3、当发生ts/adc中断(共用一条中断线),将会执行AdcTsIntHandle()中断函数,在这个程序中判断具体执行ts还是adc函数
 *
 *			3.1 若是ts中断
 *				3.1.1 触摸笔松开,则g_ts_timer_enable=0,并且等待触摸笔按下
 *					若此时发生定时器中断,则进入2.1步骤,具体如下:
 *					3.1.1.1 此时若发生定时器中断,因为g_ts_timer_enable==0,则不处理
 *					
 *				3.1.2 触摸笔按下,则g_ts_timer_enable=1,设置ADC为自动测量xy模式,并且开启ADC,触发adc中断,进入3.2
 *					若此时发生定时器中断,则进入2.1步骤,具体如下:
 *					3.1.2.1 若此时定时器发生中断,因为立刻松开,则g_ts_timer_enable=0,并且等待触摸笔按下
 *					3.1.2.2 若此时定时器发生中断,因为持续按下且g_ts_timer_enable==1,则触发adc中断,进入3.2,实现持续按下输出座标功能
 *
 *			3.2 若是adc中断,则读取座标
 *					若此时发生定时器中断,则根据情况(3.1.2.1、3.1.2.2)执行			
 */
 
#include "../s3c2440_soc.h"

#define ADC_INT_BIT (10)
#define TC_INT_BIT 	 (9)
#define INT_ADC_S    (10)
#define INT_TC    	 (9)

#define INT_ADC_TSC  (31)

/* ADCTSC's bits */
#define PEN_UP_BIT     		(1 << 8)
#define PEN_DOWN_BIT   		(0 << 8)

#define YM_OUT_ENABLE    	(1 << 7)
#define YM_OUT_DISABLE   	(0 << 7)

#define YP_OUT_ENABLE   	(0 << 6)
#define YP_OUT_DISABLE   	(1 << 6)

#define XM_OUT_ENABLE    	(1 << 5)
#define XM_OUT_DISABLE   	(0 << 5)

#define XP_OUT_ENABLE    	(0 << 4)
#define XP_OUT_DISABLE   	(1 << 4)

#define XP_POLLUP_ENABLE    (0 << 3)
#define XP_POLLUP_DISABLE   (1 << 3)

#define NO_OPR_MODE         (0 << 0)
#define WAIT_INTER_MODE     (3 << 0)
#define AUTO_PST			(1 << 2)

static volatile int g_ts_timer_enable = 0;		//标志位:0-触摸笔松开,1-触摸笔按下

void enter_wait_pen_down(void)
{
	/* 设置寄存器,控制开关,处于等待中断模式*/
	ADCTSC = PEN_DOWN_BIT | YM_OUT_ENABLE | XP_POLLUP_ENABLE | XP_OUT_DISABLE | YP_OUT_DISABLE | XM_OUT_DISABLE | WAIT_INTER_MODE;
}

void enter_wait_pen_up(void)
{
	/* 设置寄存器,控制开关,处于等待中断模式*/
	ADCTSC = PEN_UP_BIT | YM_OUT_ENABLE | XP_POLLUP_ENABLE | XP_OUT_DISABLE | YP_OUT_DISABLE | XM_OUT_DISABLE | WAIT_INTER_MODE;
}

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

/* 标志位函数:用于定时器函数中针对不同情况处理 */
static void ts_timer_enable(void)
{
	g_ts_timer_enable = 1;
}

/* 标志位函数:用于定时器函数中针对不同情况处理 */
static void ts_timer_disable(void)
{
	g_ts_timer_enable = 0;
}

/* 得到标志位函数 */
static int get_status_of_ts_timer(void)
{
	return g_ts_timer_enable;
}

void Isr_Adc(void)
{
	int x, y;

	/* 电容笔处于按下 */
	if (!(ADCDAT0 & (1 << 15))) {
		x = ADCDAT0 & 0x3ff;
		y = ADCDAT1 & 0x3ff;
		printf("x =%08d,y =%08d\n\r", x, y);
	}

	enter_wait_pen_up();	//等待下一次松开
}

void Isr_Ts(void)
{
	if (ADCDAT0 & (1 << 15)) {
		ts_timer_disable();			//标志位置0,松开
		enter_wait_pen_down();		//等待下次按下
	}
	
	else {
		ts_timer_enable();			//标志位置1,按下
		
		/* 进入自动测量模式 */
		enter_auto_measure_mode();

		/* 开启ADC */
		ADCCON |= (1<<0);	//ADC启动后此位清0
	}
}

/* 中断服务函数 */
void AdcTsIntHandle(void)
{
	if (SUBSRCPND & (1 << INT_TC))		/* 触摸屏中断 */
		Isr_Ts();
	
	if (SUBSRCPND & (1 << INT_ADC_S))	/* ADC中断 */
		Isr_Adc();

	/* 清中断 */
	SUBSRCPND = (1 << INT_TC) | (1 << INT_ADC_S);
}

void adc_ts_int_init(int irq)
{
	/* 注册中断服务函数 */
	register_irq(irq, AdcTsIntHandle); 

	/* 使能中断 */
	INTSUBMSK &= ~((1 << ADC_INT_BIT) | (1 << TC_INT_BIT));
}

void adc_ts_reg_init(void)
{
	/* [15] : ECFLG,  1 = End of A/D conversion
	 * [14] : PRSCEN, 1 = A/D converter prescaler enable
	 * [13:6]: PRSCVL, adc clk = PCLK / (PRSCVL + 1)
	 * [5:3] : SEL_MUX, 000 = AIN 0
	 * [2]   : STDBM
	 * [0]   : 1 = A/D conversion starts and this bit is cleared after the startup.
	 */
	ADCCON = (1 << 14) | (49 << 6) | (0 << 3);
	
	/*  按下触摸屏, 延时一会再发出TC中断
	 *  延时时间 = ADCDLY * 晶振周期 = ADCDLY * 1 / 12000000 = 5ms
	 */
	ADCDLY = 60000;	
}

/* 每10ms该函数被调用一次 
 * 触摸屏定时器中断函数 
 */
void touchscreen_timer_irq(void)
{
	/* 触摸笔一直处于松开,未被按下 */
	if  (get_status_of_ts_timer() == 0)
		return ;
	
	/* 触摸笔按下后松开 */
	if (ADCDAT0 & (1 << 15)) {
		ts_timer_disable();			//标志位置0,松开
		enter_wait_pen_down();		//等待下次按下
		return ;
	}

	/* 长按 */
	else {
		/* 进入自动测量模式 */
		enter_auto_measure_mode();	

		/* 开启ADC */
		ADCCON |= (1<<0);	//ADC启动后此位清0
	}
}

/* 触摸屏初始化 */
void touchsrceen_init(void)
{
	/* 设置中断 */
	adc_ts_int_init(INT_ADC_TSC);

	/* 设置触摸屏接口:寄存器 */
	adc_ts_reg_init();

	/* 注册触摸屏定时器中断处理函数 */
	register_timer("touchscreen", touchscreen_timer_irq);

	/* 让触摸屏控制器进入"等待中断(等待触摸笔按下)模式" */
	enter_wait_pen_down();
}

3、touchscreen_test.c文件修改

void touchscreen_test(void)
{
	touchsrceen_init();
}

4、Makefile文件编写

objs = start.o sdram_init.o nand_flash.o led.o uart.o main.o exception.o eint.o timer.o nor_flash.o my_printf.o string_utils.o lib1funcs.o	

objs += lcd/font.o
objs += lcd/framebuffer.o
objs += lcd/geometry.o
objs += lcd/lcd.o
objs += lcd/lcd_4.3.o
objs += lcd/lcd_controller.o
objs += lcd/lcd_test.o
objs += lcd/s3c2440_lcd_controller.o
objs += lcd/font_8x16.o

objs += adc_touchscreen/adc.o
objs += adc_touchscreen/adc_test.o
objs += adc_touchscreen/touchscreen.o
objs += adc_touchscreen/touchscreen_test.o

all: $(objs)
	arm-linux-ld -T sdram.lds $^ libgcc.a -o sdram.elf
	arm-linux-objcopy -O binary -S sdram.elf sdram.bin
	arm-linux-objdump -D sdram.elf > sdram.dis
clean:
	rm *.bin *.o *.elf *.dis
	
%.o : %.c
	arm-linux-gcc -march=armv4 -c -o $@ $<

%.o : %.S
	arm-linux-gcc -march=armv4 -c -o $@ $<
	

四、结果验证

触摸笔连续沿着一个方向滑动,通过串口输出座标信息,可以看到,此时触摸笔长按功能成功实现。
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章