ADC和触摸屏(3)-长按实现
- 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3)
- 软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.04_x64 系统
- 参考资料:开发版原理图,S3C2440A datasheet
- 源码仓库:https://gitee.com/d_1254436976/Embedded-Linux-Phase-1
目录
一、前言
在上篇的博客中,实现了触摸笔的按下松开检测,那么在这篇中,则实现触摸笔的长按功能。
二、程序设计
1、定时器中断函数
实现触摸笔的长按功能,需要使用到定时器中断,在之前的学习中,我们已经编写了timer.c文件,为了程序的拓展性,所以打算仿照内核中注册与卸载驱动的程序:
- 定义函数指针,指向具体的定时器中断函数
typedef void(*timer_fp)(void); //定义函数指针
- 定义一个结构体数组,统一管理各中定时器中断函数
#define MAX_NUM (10)
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;
}
三、程序编写
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 $@ $<
四、结果验证
触摸笔连续沿着一个方向滑动,通过串口输出坐标信息,可以看到,此时触摸笔长按功能成功实现。