韋東山嵌入式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 $@ $<
	

四、結果驗證

觸摸筆連續沿着一個方向滑動,通過串口輸出座標信息,可以看到,此時觸摸筆長按功能成功實現。
在這裏插入圖片描述

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