为什么y85开了屏幕反转果玩游戏平台一样的反转不了屏幕

电动机正反转 点动 急停_百度文库
您的浏览器Javascript被禁用,需开启后体验完整功能,
享专业文档下载特权
&赠共享文档下载特权
&10W篇文档免费专享
&每天抽奖多种福利
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
电动机正反转 点动 急停
&&PLC 电动机 正反转 点动 急停
阅读已结束,下载本文需要
想免费下载更多文档?
定制HR最喜欢的简历
下载文档到电脑,同时保存到云知识,更方便管理
加入VIP
还剩16页未读,
定制HR最喜欢的简历
你可能喜欢第018课 ADC和触摸屏硬件原理详解及裸机编程
第001节_ADC硬件原理
模数转换器即A/D转换器,或简称ADC,通常是指一个将模拟信号转变为数字信号的电子元件。
通常的模数转换器是把经过与标准量比较处理后的模拟量转换成以二进制数值表示的离散信号的转换器。
故任何一个模数转换器都需要一个参考模拟量作为转换的标准,比较常见的参考标准为最大的可转换信号大小。而输出的数字量则表示输入信号相对于参考信号的大小。
如图,是把可变电阻上的电压值变换的模拟信号通过ADC转换,输出数字信号。
对于数字信号我们需要得到它的几个属性
*用多少位来存储这个数据(假设10bit)。
*它对应的电压是多少伏(模拟信号输入的最大值是多少)我们就可以根据模拟信号(电压)的最大值,来计算出对应的数值。
*采样/转换速度。
对于程序员,我们不关心ADC的内部机制,我们只关心:
*怎么启动ADC
*启动之后怎么得到数据,
总之:我们都是通过寄存器操作的。
从图1-1-1可以看出ADC有8个多路选择器,显然,以后我们写程序的时候,我们可以8个多路选择之一,
下面是编写程序要做的步骤:
确定是哪一路信号:设置8:1MUX,选择要测量哪一个引脚,(看原理图选择要测量的引脚)
设置工作时钟(从工作室中,可以算出转换一次,需要多长时间)
读状态,判断ADC转换是否成功。
ADC寄存器介绍
1.ADC 控制寄存器(ADCCON)
ADCCON控制寄存器,用于标志转换是否完成,控制是否使能预分频器,输入通道选择,工作模式,ADC是否启动。它的各位含义如下图所示。
2.ADC 启动延时寄存器(ADCDLY)
ADCDLY 启动延时寄存器用于启动或初始化延时寄存器。它的各位含义如下图所示
3.ADC 转换数据寄存器(ADCDAT0)
ADCDAT0转换数据寄存器,本节中只用到该寄存器的前10位(用于保存转换后的结果)。
第002节_ADC编程
编程步骤:
在串口上显示出来。
一.初始化ADC
下面的函数实现对ADC的初始化。
void adc_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
: 1 = A/D conversion starts and this bit is cleared after the startup.
ADCCON = (1&&14) | (49&&6) | (0&&3);
ADCDLY = 0xff;
第12行:配置ADCCON寄存器,使能A/D 转换器预分频器,设置A/D 转换器预分频值,上拉使能。
第14行:设置ADC 转换启动延时值。
在这个读函数中启动ADC,并且等待ADC转换成功。然后返回数据,
int adc_read_ain0(void)
ADCCON |= (1&&0);
while (!(ADCCON & (1&&15)));
return ADCDAT0 & 0x3ff;
第20行:启动ADC。
第22行:等待A/D转换结束(ADCCON第15位置1),
第24行:返回转换的值。(ADCDAT0寄存器的前10位,是保存转换后的值)。
三.ADC测试
函数代码如下: 函数功能:在串口/LCD上打印ADC转换后的结果。
void adc_test(void)
adc_init();
val = adc_read_ain0();
vol = (double)val/1023*3.3;
vol = vol -
n = vol * 1000;
printf("vol: %d.%03dv", m, n);
*第11行:初始化ADC.
*第15行:把ADC转换得到的值赋值给变量val.
*第16行:把变量val的值转化为电压值。
*第17行:取vol整数部分赋值给变量m。
*第18行:取vol的小数部分赋值给vol。
把生成的二进制文件烧录到开发板上,接上SPI模块,旋转可变电阻就可以在串口上看到电压值发生变化。
第003节_电阻触摸屏硬件原
这节课我们来讲电阻触摸屏的硬件原理
假设有一个比较长的电阻,电阻是R 上面接3.3V电压,下面接地
假设整个电阻的阻值是R某一个触电它的阻值是R1
根据欧姆定律
3.3v/R = V/R1
V=3.3 *(R1/R)
假设R1是x坐标
R的长度是l
这个电阻非常的均匀,那么这个电压就等于 3.3V * (x / l)
这个电压和这个触电的x坐标有一个线性关系
我使用ADC把这个电压算出来,就可以间接得到这个触电的x坐标
电阻触摸屏就是使用欧姆定律使用电阻原理作出来的
可以上百度图片搜索触摸屏,就知道了触摸屏的样子,它是一个透明的薄膜,注意 LCD是LCD 触摸屏是触摸屏它是两个设备,
我们只不过是把触摸屏做的和LCD大小一样,粘在LCD上面,
实际上触摸屏是由两层膜组成,他们靠的非常近
上面这层右边引出来,代表xp ,p代表正极
上面这层左边引出来,代表xm, m代表负极
下面这层膜 前面这条边引出来为yp,后面这层边为ym
假设我们手指要点击触摸屏,那么上下就会粘贴在一起,我怎么算出这个 x y点的坐标呢?
测量触电x坐标:
xp接3.3v,xm接GND
yp,ym不接电源
上下膜连接在一起,我就可以通过yp测量这个触电的电压
这个yp就像探测一样,从前面的原理我们可以知道,当这个触电越靠近左边这个电压越小,越靠近右边电压越大
这个yp的电压就可以认为是这个触电的坐标(x坐标)
类似的我们怎么测量触电y坐标
类似的xp xm不接电源,同样yp接3.3v, ym接GND,这时候电流就从 yp这里流向ym,让后我们就可以测量xp电压
当按下屏幕时,上下两层膜链接在一起,这个xp就像探针一样,这个触电越靠近yp电压值越大,越靠近ym电压值越小
yp接3.3V ym接GND,xp xm不接电源
测量xp电压,就是y坐标
注意 x y坐标都是电压值,不是屏幕上480 * 272 这些值,我们需要把电压值转换为坐标值,这需要经过一些转换
我们测量xp yp可以得到触点的两个方向的电压值,这些电压值和坐标是线性关系
我们现在总结下使用触摸屏的流程
&1&按下触摸屏 按下触摸屏时,对于一个高效的系统,产生中断,这是触摸屏中断
&2&在触摸中断程序中 启动ADC,(获得数据,xy坐标)启动ADC就开始模数转换,不可能瞬间完成,
&3&ADC完成, 产生中断
&4&ADC中断中读取x y坐标,我们来想想,在这个流程里,启动触摸屏的源头是按下触摸屏,那如果长按触摸屏,我按下之后一直不松开 滑动手指呢
那么谁来触发后续的多次ADC转换呢 不可能只启动一次吧,
为了支持 长按 滑动操作,我们需要启用定时器.
&5& 启动定时器
&6& 定时器中断发生,判断触摸屏是否仍被按下,如果按下就循环上述过程
&6.1&在触摸中断程序中 启动ADC,(获得数据,xy坐标)启动ADC就开始模数转换,不可能瞬间完成
&6.2&ADC完成, 产生中断
&6.3&ADC中断中读取x y坐标,)
&7& 松开结束一个流程
这就是整个触摸屏的使用流程
在14章里讲解了触摸屏,他抽象了几张图
平时的时候上下两层膜并不连接,我们按下触摸屏的时候就会产生中断,那么你怎么知道产生中断,肯定是由某个引脚的电平发生变化,平时 Y_ADC/xp是高电平,按下之后Y_ADC就接地了,就是被拉低了,就产生了低电平
产生低电平后就知道触摸屏被按下了,这个时候就需要测量电压值读取x坐标,XP XM通电我就测量YP的电压,这不就是 x 点的坐标
YP YM 通电,按下后XP通电,这不就是y点的坐标么
第004节_S3C2440触摸屏接口
回顾上节触摸屏使用原理
在不使用触摸屏的时候,必须要把 S1 S2 S3断开,S4 S5闭合,只有这样当我按下触摸屏,上面的电平才能从高变低,会产生一个中断信号,而当我去读取X坐标的值时
必须让S1 S3闭合,这样电流才可以通过,同时让S2 S4 S5断开,这时候YP这层膜就相当于探针一样去测量电压
当我读取y坐标值
必须让S2 S4闭合,这样电流才可以流 下来,同时S1 S3 S5断开,这个时候XP这层膜就相当于探针一样,我可以来测量这里的电压,从而得到Y坐标的电压值
在测量x y坐标时,这个S5上拉电阻都要断开,
我们需要控制这几个开关,实际上2440就提供了这几个开关的控制方法,
打开2440的芯片手册看触摸屏时怎么操作的,
从440到450总共10页不到,
我们看有一个8:1 MUX的多路选择器,以及XP YP
442页触摸屏接口模式
正常模式,在上节视频中我们有讲解过
x y分离转换模式,
看看我们的X Y坐标原理图,可以单独转换X坐标
单独转换Y坐标
换句话说就是逐个去测量X Y坐标,
他首先会启动X坐标的ADC转换,转换成功后数据会保存在ADCDAT0里,同时会产生一个中断,在这个中断服务程序里,就可以把X坐标读取出来,让后可以启动Y坐标的转换,转换成功后数据会保存在ADCDAT,同时会产生一个中断,进入这个中断把Y坐标读取出来
测量一次会产生2个中断,一个时X坐标中断,一个是Y坐标中断
自动的或连续的X/Y坐标转换模式
也就是说不需要单独控制,不需要单独去读取X坐标Y坐标,可以设置寄存器,让它一次性的测量X坐标测量Y坐标,X坐标保存在ADCDAT0 Y坐标保存在ADCDAT1,最后产生一个中断,也就是读取X/Y坐标只需要产生一次中断
等待中断模式
所谓等待中断模式,就是等待按下或者等待松开
对于下面这幅图,我按下的时候XP从高电平变为低电平,松开时,XP从低电平变为高电平,这就是按下松开都可以检测到
我们要等待按下或者松开时 需要设置rADCTSC =0xd3这个值
Standby Mode静默模式/省电模式(我们不关心这个)
443页编程要点
*AD转换数据时可以通过中断或者查询模式来得到数据,使用中断模式时,从AD转换开始,到得到数据可能会有些延迟,因为中断服务程序的进入和退出需要一定的时间,(也就是说,如果你对数据转换的速度要求的非常高,就可以使用查询方式),可以查询ADCCON[15]来判断是否转换结束
剩下就是寄存器操作
ECFLG状态位 AD转换是否结束
PRSCEN 使能ADC转换
PRSCVL 设置A/D转换预分频值
SEL_MUX选择输入通道,后面我们使用自动转换XY坐标,所以这里不需要设置
ENABLE_START 启动转换
ADCTSC这个寄存器是重要的
UD_SEN Bit8是用来判断触摸屏是被按下还是被松开
0表明被按下,1表明被松开
YM_SEN Bit7
YM开关使能控制S4
YP_SEN Bit6 YP开关
0表示闭合 1 表示断开
*寄存器位的含义不同
XM_SEN Bit5 XM开关
0 断开 1 闭合
XP_SEN Bit4 XP开关
0 闭合 1 断开
PULL_UP Bit3 控制S5开关
0 上拉(闭合)
AUTO_PST Bit2 自动连续转换X坐标Y坐标
上节视频里我们设置是 0
正常的ADC转换
如果需要连续转换ADC坐标的话,需要设置为1 ,如果需要手动转换ADC坐标的话,需要设置为0
XY_PST Bit[1:0]
对于手动转换X Y坐标我们需要手动设置XY_PST 里面的位,是测量X坐标还是测量Y坐标,
也可以设置这两位等于11 让其等于等待模式,
也就是等待触摸屏被按下或者被松开.
如果设置自动连续转换的话,Bit2 AUTO_PST设置为1
XY_PST设置为00
如果使用手动转换的话设置AUTO_PST为0
XY_PST设置为01 手动转换X坐标模式
或者设置为10 Y坐标转换模式
447页ADCDATA0 ADC数据寄存器
UPDOWN Bit15 可以读取这一位去判断触摸屏是按下还是松开
AUTO_PST Bit14 自动测量
XY_PST Bit[13:12] 和上面ADCTSC寄存器中 AUTO_PST Bit2
XY_PST Bit[1:0]原理相同
XPDATA Bit[9:0]最低10位用来保存ADC的值
448页‘ADCDAT1寄存器 和ADCDAT0功能一样的,只不过保存的数据不同
这个的低10位是用来保存 Y坐标的值
接下来是ADCUPDN触摸屏按下或者松开检查寄存器
TST_UP Bit1 触摸屏松开中断产生
TST_DN Bit0 触摸屏按下中断产生
手册看完了,涉及到中断,我们看下这个图
它会涉及两个中断,按下或者松开,触摸笔的状态中断,另外一个启动ADC以后,ADC结束时也会产生一个中断,但是这个手册里没有看到中断的是能寄存器
那我们猜测一下,ADC模块或者触摸屏模块一定会发出中断
首先是ADC或者触摸屏产生中断,通过中断控制器发送中断给CPU
肯定有寄存器禁止/使能ADC或者触摸屏中断
我们看看中断控制器芯片手册中都需要设置什么
ADC结束中断或者触摸屏中断,看来他们合起来用一个中断
既然合并必然还会有一个寄存器来分辨到底是ADC还是触摸屏发生的中断变化
SRCPND寄存器
31位为ADC中断
设置Bit[31]
INTMOD寄存器
来决定是普通中断还是快中断模式
设置Bit[31]
INTMSK寄存器
用来表示是否屏蔽这个中断
设置Bit[31]
优先级我们不需要设置
设置Bit[31]表示中断是否正在处理
设置Bit[31]
到底是ADC中断还是触摸屏中断,肯定有其他寄存器可以设置
SUBSOURCE PENDING寄存器
INT_ADC_S Bit[10]表示ADC中断
INT_TC Bit[9]表示触摸屏中断
应该也是同样的位
INT_ADC_S Bit[10]表示ADC中断激活/屏蔽
INT_TC Bit[9]表示触摸屏中断激活/屏蔽
我们可以通过INTSUBMSK来屏蔽ADC中断或者TouchScreen中断
当然也可以是能某个中断
可以通过SUBSRCPND来分辨到底产生那个中断
INTSUBMSK 和SUBSOURCPND这两个寄存器都会汇集到一起
变成一个叫做INT_ADC的中断来发送给CPU
框图就是这样
我们怎么写程序?
写出一个框架
1 初始化ADC/TouchScreen接口ADCCON时钟接口
2 一开始触摸屏是没有被按下的,设置TS处于等待中断模式
3 设置中断
INTSUBMSK使能ADC中断和触摸屏中断,还有INTMSK设置这个寄存器使能ANT_ADC让他能够发给CPU
4 按下触摸屏,进入TS中断
4.1 进入自动采集模式(自动转换XY坐标)
4.2 启动ADC
5.转换完之后产生ADC中断
5.1 读数据
5.2 再次进入 “’等待中断”’模式
5.3 启动定时器,处理长按或者滑动
6 定时器中断
6.1 判断是否松开,若松开结束
6.2 若按下重新执行 4.2启动ADC步骤
第005节触摸屏编程按下松开检测
开始触摸屏编程,关于触摸屏编程大概会分为3个小节
第006节_触摸屏编程_ADC中断
第007节触摸屏编程定时器程序优化
参考《嵌入式Linux应用开发完全手册》第14章 ADC和触摸屏接口
可以参考下面这张图
看懂这张图的关键点在于
里面有个中断程序 AdcTsIntHandle
它是总的中断,这里面要分辨
如果是ADC中断 那么就调用Isr_adc来处理中段
如果是触摸屏中断,那么就调用Isr_tc中断,
这些都是总中断具体的中断.
我们看看是怎么做的
*一开始设置中断
*初始化触摸屏控制器,进入等待中断模式
*这个时候如果按下触摸屏就会进入Pen Down中断
*就会进入AdcTsIntHandle这个总中断函数
*这里面分辨是按下触摸屏
*进入自动(连续) X/Y轴坐标转换模式,启动ADC,
*ADC结束之后会产生一个ADC中断
*又再次进入这个AdcTsIntHandle总中断
*这里面分辨是ADC中断,这里面调用Isr_Adc
*我可以读出这里面的数据,再次设置寄存器
*进入等待Pen UP中断模式
*松开触摸笔会再次产生一个中断
*进入总中断AdcTsIntHandle这里面分辨,原来是松开了触摸笔,再次调用Isr_tc
*这里面又会设置进入等待Pen Down中断模式
我们开始写代码,再上一个视频ADC代码上进行修改
002_touchscreen_018_005/adc_touchscreen
我们在adc_touchscreen目录下添加几个文件
touchscreen_test.c
touchscreen.c
我们打开touchscreen.c文件
void touchscreen_init(void)
看看上面流程图
我们设置中断处理函数
void AdcTsIntHandle(void)
看一下之前我们是怎么写中断的,看一下interrupt.c文件
void key_eint_irq(int irq)
有个中断号
那么我们也定义个int irq参数
void AdcTsIntHandle(int irq)
我们在这个里面分辨一下
if (SUBSRCPND & (1&&TC_INT_BIT))
Isr_Adc();
我们等会实现这两个函数
我们继续写代码
void touchscreen_init(void)
看看上面流程图
adc_ts_int_init();
adc_ts_reg_init();
enter_wait_pen_down_mode();
void adc_ts_int_init(void)
register_irq(irq, irq_handle);
中断号是多少?
打开芯片手册,找到中断控制器
我们是31号中断
register_irq(31, AdcTsIntHandle);
怎么使能中断?
我们需要把 INTSUBMISK寄存器的Bit9 Bit10设置为0
#define ADC_INT_BIT (10)
#define TC_INT_BIT
使能中断,清零
INTSUBMSK &= ~((1&
void AdcTsIntHandle(int irq)
Isr_Adc();
如何进行分辨
if (SUBSRCPND & (1&&TC_INT_BIT))
if (SUBSRCPND & (1&&ADC_INT_BIT))
Isr_Adc();
#include "../s3c2440_soc.h"
#define ADC_INT_BIT (10)
#define TC_INT_BIT
#define INT_ADC_TC
#define WAIT_PEN_DOWN
#define WAIT_PEN_UP
#define YM_ENABLE
#define YM_DISABLE
#define YP_ENABLE
#define YP_DISABLE
#define XM_ENABLE
#define XM_DISABLE
#define XP_ENABLE
#define XP_DISABLE
#define PULLUP_ENABLE
#define PULLUP_DISABLE
#define AUTO_PST
#define WAIT_INT_MODE
#define NO_OPR_MODE
void enter_wait_pen_down_mode(void)
ADCTSC = WAIT_PEN_DOWN | PULLUP_ENABLE | YM_ENABLE | YP_DISABLE | XP_DISABLE | XM_DISABLE | WAIT_INT_MODE;
void enter_wait_pen_up_mode(void)
ADCTSC = WAIT_PEN_UP | PULLUP_ENABLE | YM_ENABLE | YP_DISABLE | XP_DISABLE | XM_DISABLE | WAIT_INT_MODE;
读一下寄存器,找到触摸屏的寄存器触摸笔,按下松开状态寄存器
我们可以读它 Bit1表示up Bit0表示down
void Isr_Tc(void)
printf("ADCUPDN = 0x%x, ADCDAT0 = 0x%x, ADCDAT1 = 0x%x, ADCTSC = 0x%x\n\r", ADCUPDN, ADCDAT0, ADCDAT1, ADCTSC);
if (ADCDAT0 & (1&&15))
printf("pen up\n\r");
enter_wait_pen_down_mode();
printf("pen down\n\r");
enter_wait_pen_up_mode();
void AdcTsIntHandle(int irq)
if (SUBSRCPND & (1&&TC_INT_BIT))
SUBSRCPND = (1&&TC_INT_BIT) | (1&&ADC_INT_BIT);
void adc_ts_int_init(void)
SUBSRCPND = (1&&TC_INT_BIT) | (1&&ADC_INT_BIT);
register_irq(31, AdcTsIntHandle);
INTSUBMSK &= ~((1&&ADC_INT_BIT) | (1&&TC_INT_BIT));
void adc_ts_reg_init(void)
ADCCON = (1&&14) | (49&&6) | (0&&3);
ADCDLY = 0xff;
void touchscreen_init(void)
adc_ts_reg_init();
printf("ADCUPDN = 0x%x, SUBSRCPND = 0x%x, SRCPND = 0x%x\n\r", ADCUPDN, SUBSRCPND, SRCPND);
adc_ts_int_init();
enter_wait_pen_down_mode();
第006节_触摸屏编程_ADC中断
这节课我们加上ADC中断把触点的xy坐标读出来
查看touchscreen.c
写出这个自动测量的函数
void enter_auto_measure_mode(void)
设置AUTO_PST =1
XY_PST = 00
ADCTSC = AUTO_PST | NO_OPR_MODE;
//现在是自动测量,我们没有机会分别设置这些开关
进入中断处理函数
void AdcTsIntHandle(int irq)
if (SUBSRCPND & (1&&TC_INT_BIT))
if (SUBSRCPND & (1&&ADC_INT_BIT))
Isr_Adc();
SUBSRCPND = (1&&TC_INT_BIT) | (1&&ADC_INT_BIT);
进入触摸屏中断处理函数
void Isr_Tc(void)
if (ADCDAT0 & (1&&15))
enter_wait_pen_down_mode();
enter_auto_measure_mode();
ENABLE_START = 1就可以了
ADCCON |= (1&&0);
/* 启动ADC */
Adc中断处理函数
void Isr_Adc(void)
进入adc中断后,等待触摸笔松开
int x = ADCDAT0 & 0x3ff;
int y = ADCDAT1 & 0x3ff;
printf("x = %08d, y = %08d\n\r", x, y);
enter_wait_pen_up_mode();
实验发现打印一堆乱码
应该是printf函数出了问题
打开my_printf.c文件,找到printf函数
应该是处理第二个数据的时候,没有设置初始值
/*reference :
int vprintf(const char *format, va_list ap); */
static int my_vprintf(const char *fmt, va_list ap)
char lead=' ';
maxwidth=0;
for(; *fmt != '\0'; fmt++)
if (*fmt != '%') {
outc(*fmt);
//碰到 % 就重新处理, 初始值应该重新设置初始值上去
maxwidth=0;
//format : %08d, %8d,%d,%u,%x,%f,%c,%s
if(*fmt == '0'){
lead = '0';
while(*fmt &= '0' && *fmt &= '9'){
maxwidth *=10;
maxwidth += (*fmt - '0');
switch (*fmt) {
case 'd': out_num(va_arg(ap, int),
10,lead,maxwidth); break;
case 'o': out_num(va_arg(ap, unsigned int),
8,lead,maxwidth); break;
case 'u': out_num(va_arg(ap, unsigned int), 10,lead,maxwidth); break;
case 'x': out_num(va_arg(ap, unsigned int), 16,lead,maxwidth); break;
case 'c': outc(va_arg(ap, int
)); break;
case 's': outs(va_arg(ap, char *)); break;
outc(*fmt);
重新烧写执行,发现数据变化幅度很大,但至少Adc已经有输出
我们需要解决输出值不线性的问题
到底是触摸屏质量问题,还是Adc转化精度问题,
觉得应该是触摸屏电压不稳定
之前不知道DELAY寄存器是用来干嘛的
等待中断模式时,当触摸笔按下时我们会产生中断,但是可以通过 DELAY来延时产生中断
在前面有一张图
按下触摸笔,延迟A
才可以产生中断,你才可以测量X Y坐标
A = D(晶振的周期)
D就是 DELAY就是那个寄存器的值
晶振周期时12M
我们需要设置一下
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
: 1 = A/D conversion starts and this bit is cleared after the startup.
ADCCON = (1&&14) | (49&&6) | (0&&3);
按下触摸屏, 延时一会再发出TC中断
* 10ms为120000
延时时间 = ADCDLY * 晶振周期 = ADCDLY * 1 /
ADCDLY = 60000;
再次烧写,发现数据并不规律
我们需要再次改进程序
我们按下触摸屏会产生触摸屏中断,启动自动测量,启动Adc,Adc成功后会进入Adc中断,在函数中打印数据
也许测量过程很长
我们就需要判断
void Isr_Adc(void)
int x = ADCDAT0;
int y = ADCDAT1;
if (!(x & (1&&15)))
x &= 0x3ff;
y &= 0x3ff;
printf("x = %08d, y = %08d\n\r", x, y);
enter_wait_pen_up_mode();
发现X Y轴输出有问题
厂家把X Y轴搞反了
TSYP TSXP接反
TSYM TSXM接反
我们后面使用触摸屏时会使用软件处理这点,不会导致任何问题 
有一个缺点
我们按下触摸屏会输出一个数据,再按下触摸屏又输出一个数据
我长按并没有输出数据,我滑动也没有输出数据
我们需要使用定时器改进这个问题
各种方向的旋转都可以由软件转换
我们需要把触摸屏的坐标TS XY坐标转换成LCD的XY坐标
需要用应用程序做
我们常使用Tslib库来做,这些旋转倒置都没有问题
第007节触摸屏编程定时器程序优化
有一个缺点
我们按下触摸屏会输出一个数据,再按下触摸屏又输出一个数据
我长按并没有输出数据,我滑动也没有输出数据
我们需要使用定时器改进这个问题
这个处理流程是怎么样的?
按下期间启动定时器
定时器每过10ms / 20ms就中断一次
在中断函数里测量触电的XY坐标
这样就可以得到连续的数据
打开定时器Timer.c
#include "s3c2440_soc.h"
#define TIMER_NUM
#define NULL
((void *)0)
typedef void(*timer_func)(void);
typedef struct timer_desc {
}timer_desc, *p_timer_
timer_desc timer_array[TIMER_NUM];
int register_timer(char *name, timer_func fp)
for (i = 0; i & TIMER_NUM; i++)
if (!timer_array[i].fp)
[i].name =
timer_array[i].fp
return -1;
我们不需要使用Timer定时器的时候unregister_timer函数,
考虑到我们需要从数组里面把这个Timer去掉,我们怎么找到这个Timer?
传入一个函数指针,以后卸载使用名字找到对应的项.
void unregister_timer(char *name)
for (i = 0; i & TIMER_NUM; i++)
if (!strcmp(timer_array[i].name, name))
timer_array[i].name = NULL;
timer_array[i].fp
return -1;
应该让其从某个数组里面把需要定时器处理的函数依次执行,这样做,我们以后添加定时器处理函数时就不需要修改Timer.c
void timer_irq(void)
for (i = 0; i & TIMER_NUM; i++)
//判断指针是否为空,如果不是空的话就继续执行timer_array[i].fp();这个函数
if (timer_array[i].fp)
timer_array[i].fp();
如果想继续点灯的话,需要单独注册led_timer_irq
在led.c文件里注册led_timer_irq
把这个函数放在led_timer_irq函数下面,防止编译错误,每10ms改函数被调用一次.
int led_init(void)
GPFCON &= ~((3&&8) | (3&&10) | (3&&12));
((1&&8) | (1&&10) | (1&&12));
register_timer("led", led_timer_irq);
void led_timer_irq(void)
static int timer_num = 0;
static int cnt = 0;
timer_num++;
if (timer_num & 50)
timer_num = 0;
GPFDAT &= ~(7&&4);
GPFDAT |= (tmp&&4);
修改main.c
int main(void)
led_init();
key_eint_init();
timer_init();
puts("\n\rg_A = ");
printHex(g_A);
puts("\n\r");
lcd_test();
touchscreen_test();
while (1);
修改timer.c文件
void timer_init(void)
TCFG0 = 49;
TCFG1 &= ~0xf;
TCFG1 |= 3;
TCNTB0 = 625;
TCON |= (1&&1);
TCON &= ~(1&&1);
TCON |= (1&&0) | (1&&3);
register_irq(10, timer_irq);
烧写到nandflash发现无输出,可能是前重定位前的代码超出了4k,所以我们使用Norflash启动,发现可以正常运行
我们修改Makefile把负责重定位代码往前移,其他无关代码往后放,接着看star.S
.global _start
ldr pc, und_addr
ldr pc, swi_addr
ldr pc, irq_addr
//我们需要把这些异常往后放
.word do_und
.word do_swi
.word do_irq
ldr r0, =0x
ldr r1, =0
str r1, [r0]
ldr r0, =0x4C000000
ldr r1, =0xFFFFFFFF
str r1, [r0]
ldr r0, =0x4C000014
ldr r1, =0x5
str r1, [r0]
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000
//R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
ldr r0, =0x4C000004
ldr r1, =(92&&12)|(1&&4)|(1&&0)
str r1, [r0]
mov r1, #0
ldr r0, [r1]
str r1, [r1]
ldr r2, [r1]
cmp r1, r2
ldr sp, =0x+4096
moveq sp, #4096
/* nand启动 */
streq r0, [r1]
bl sdram_init
//bl sdram_init2
bl copy2sdram
bl clean_bss
mrs r0, cpsr
bic r0, r0, #0xf
/* 修改M4-M0为0b10000, 进入usr模式 */
bic r0, r0, #(1&&7)
/* 清除I位, 使能中断 */
msr cpsr, r0
ldr sp, =0x33f00000
ldr pc, =sdram
bl uart0_init
init.c 需要放在前面
sdram_init初始化也放前面
bl uart0_init
这就跳到sdram了,重定位后就随便操作
修改Makefile
我们把 start.o init.o nand_flash.o放在最前面
objs = start.o init.o nand_flash.o led.o uart.o main.o exception.o interrupt.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 -Ttext 0 -Tdata 0x
start.o led.o uart.o init.o main.o -o sdram.elf
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
rm -f *.bin $(objs) *.elf *.dis
arm-linux-gcc -march=armv4 -c -o $@ $&
arm-linux-gcc -march=armv4 -c -o $@ $&
这节课讲定时器的优化,
下节课讲怎么使用定时器来改进触摸屏
第008节触摸屏编程使用定时器支持长按
可以使用定时器把长按或者滑动触摸屏的值读出来,我们按下触摸屏就会产生触摸屏中断,这个时候可以启动ADC,ADC成功后再次产生中段,在这个中断中启动定时器,我们也可以在触摸屏中断里启动定时器.
#include "../s3c2440_soc.h"
#define ADC_INT_BIT (10)
#define TC_INT_BIT
#define INT_ADC_TC
#define WAIT_PEN_DOWN
#define WAIT_PEN_UP
#define YM_ENABLE
#define YM_DISABLE
#define YP_ENABLE
#define YP_DISABLE
#define XM_ENABLE
#define XM_DISABLE
#define XP_ENABLE
#define XP_DISABLE
#define PULLUP_ENABLE
#define PULLUP_DISABLE
#define AUTO_PST
#define WAIT_INT_MODE
#define NO_OPR_MODE
static volatile int g_ts_timer_enable = 0;
void adc_ts_int_init(void)
SUBSRCPND = (1&&TC_INT_BIT) | (1&&ADC_INT_BIT);
register_irq(31, AdcTsIntHandle);
INTSUBMSK &= ~((1&&ADC_INT_BIT) | (1&&TC_INT_BIT));
void touchscreen_init(void)
adc_ts_reg_init();
printf("ADCUPDN = 0x%x, SUBSRCPND = 0x%x, SRCPND = 0x%x\n\r", ADCUPDN, SUBSRCPND, SRCPND);
adc_ts_int_init();
register_timer("touchscreen", touchscreen_timer_irq);
enter_wait_pen_down_mode();
进入touchscreen.c adc中断处理函数
void Isr_Adc(void)
int x = ADCDAT0;
int y = ADCDAT1;
if (!(x & (1&&15)))
x &= 0x3ff;
y &= 0x3ff;
printf("x = %08d, y = %08d\n\r", x, y);
ts_timer_enable();
ts_timer_disable();
enter_wait_pen_down_mode();
enter_wait_pen_up_mode();
void touchscreen_timer_irq(void)
if (get_status_of_ts_timer() == 0)
if (ADCDAT0 & (1&&15))
ts_timer_disable();
enter_wait_pen_down_mode();
enter_auto_measure_mode();
ADCCON |= (1&&0);
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_
我们回顾一下处理过程,根据流程图分析
首先从main.c函数开始
int main(void)
led_init();
key_eint_init();
timer_init();
puts("\n\rg_A = ");
printHex(g_A);
puts("\n\r");
lcd_test();
touchscreen_test();
while (1);
进入touchscreen_test.c文件执行init初始化程序
void touchscreen_test(void)
touchscreen_init();
进入touchscreen.c文件
void touchscreen_init(void)
adc_ts_reg_init();
printf("ADCUPDN = 0x%x, SUBSRCPND = 0x%x, SRCPND = 0x%x\n\r", ADCUPDN, SUBSRCPND, SRCPND);
adc_ts_int_init();
enter_wait_pen_down_mode();
void adc_ts_reg_init(void)
ADCCON = (1&&14) | (49&&6) | (0&&3);
ADCDLY = 60000;
void adc_ts_int_init(void)
SUBSRCPND = (1&&TC_INT_BIT) | (1&&ADC_INT_BIT);
register_irq(31, AdcTsIntHandle);
INTSUBMSK &= ~((1&&ADC_INT_BIT) | (1&&TC_INT_BIT));
void AdcTsIntHandle(int irq)
if (SUBSRCPND & (1&&TC_INT_BIT))
if (SUBSRCPND & (1&&ADC_INT_BIT))
Isr_Adc();
SUBSRCPND = (1&&TC_INT_BIT) | (1&&ADC_INT_BIT);
void touchscreen_timer_irq(void)
if (get_status_of_ts_timer() == 0)
if (ADCDAT0 & (1&&15))
ts_timer_disable();
enter_wait_pen_down_mode();
enter_auto_measure_mode();
ADCCON |= (1&&0);
第009节触摸屏编程较准原理
我们需要校准触摸屏,所谓校准就是找到一个公式把电压值转换为坐标值
触摸屏和LCD是两个东西,触摸屏覆盖在LCD上.
问:得到触电的(x1,y1)怎么换算出LCD的坐标值 (X,Y)?
x=479-0/x2-x1 * (x3-x1) + 0
x=长度的比例
x1 0 原点的触摸屏/LCD坐标
我们只需要确定两个点就可以把lcd坐标确定下来,但是我们可以做的更好
假设由于制作工艺问题,导致触摸屏和LCD坐标并不相同,需要其他公式计算
s1’是TS上X轴两个点的距离
s1 是LCD上X轴两个点的距离
Kx= LCD距离/触摸屏距离
= (s1 + s2) / (s1’ + s2’)
= 2s/(s1’ + s2’)
TS距离是d1’
LCD距离是d1
Ky=(d1 + d2) / (d1’ + d2’)
= 2d / (d1’ + d2’)
我们现在有了斜率,给定一个坐标,我们需要需要原点的触屏LCD坐标
原点我们选在最中间
可以忽略掉上下左右的偏差
原点坐标在触摸屏上是xc' yc',在LCD上是 xc yc ,那我们的校准公式,对于给定的x3,我们如何求出x
X= (x3 - xc’ ) * Kx + xc
对于给定的y’我们如何算出Y轴坐标?
y = (y’ - yc’) * Ky + yc
我们需要点击触摸屏上这5个点,同时需要把这五个点坐标打印显示出来.
这节视频我们讲的时校准原理
第010节触摸屏编程较准与画线编
这个程序我们怎么写
我们需要得到这5个点的坐标
给这5个点分别设置为ABCDE
在A点显示 +
记录触摸屏的坐标
在BCDE上循环操作,显示点击读取的操作
根据这些数据,确定公式
以后得到TS触点时,可转换出LCD坐标
我们需要实现这几个函数
在x y 中显示 fb_disp_cross(int x , int y)
如何记录 ts_read_raw ,读到原始数据,根据这些数据,确定公式
ts_calibrate
如何转换出LCD坐标?
我们实现这几个函数
*我们先实现 +
*我们既然画线就在geometry.c中实现
void fb_disp_cross(int x, int y, unsigned int color)
draw_line(x-10, y, x+10, y, color);
draw_line(x, y-10, x, y+10, color);
编辑touchscreen.c
static int g_ts_x;
static int g_ts_y;
static volatile char g_ts_data_valid = 0;
void Isr_Adc(void)
int x = ADCDAT0;
int y = ADCDAT1;
if (!(x & (1&&15)))
x &= 0x3ff;
y &= 0x3ff;
report_ts_xy(x, y);
ts_timer_enable();
ts_timer_disable();
enter_wait_pen_down_mode();
enter_wait_pen_up_mode();
void report_ts_xy(int x, int y)
if (g_ts_data_valid == 0)
g_ts_data_valid = 1;
void ts_read_raw(int *px, int *py)
while (g_ts_data_valid == 0);
*px = g_ts_x;
*py = g_ts_y;
g_ts_data_valid = 0;
下面我们实现ts_calibrate校准函数,在tslib.c文件中
static double g_
static double g_
static int g_ts_xc, g_ts_
static int g_lcd_xc, g_lcd_
static int g_ts_xy_swap = 0;
把ABCDE这5个点都实现
void ts_calibrate(void)
unsigned int fb_
int xres, yres,
int a_ts_x, a_ts_y;
int b_ts_x, b_ts_y;
int c_ts_x, c_ts_y;
int d_ts_x, d_ts_y;
int e_ts_x, e_ts_y;
int ts_s1, ts_s2;
int lcd_s;
int ts_d1, ts_d2;
int lcd_d;
get_lcd_params(&fb_base, &xres, &yres, &bpp);
get_calibrate_point_data(50, 50, &a_ts_x, &a_ts_y);
get_calibrate_point_data(xres-50, 50, &b_ts_x, &b_ts_y);
get_calibrate_point_data(xres-50, yres-50, &c_ts_x, &c_ts_y);
get_calibrate_point_data(50, yres-50, &d_ts_x, &d_ts_y);
get_calibrate_point_data(xres/2, yres/2, &e_ts_x, &e_ts_y);
g_ts_xy_swap = is_ts_xy_swap(a_ts_x, a_ts_y, b_ts_x, b_ts_y);
if (g_ts_xy_swap)
swap_xy(&a_ts_x, &a_ts_y);
swap_xy(&b_ts_x, &b_ts_y);
swap_xy(&c_ts_x, &c_ts_y);
swap_xy(&d_ts_x, &d_ts_y);
swap_xy(&e_ts_x, &e_ts_y);
ts_s1 = b_ts_x - a_ts_x;
ts_s2 = c_ts_x - d_ts_x;
lcd_s = xres-50 - 50;
ts_d1 = d_ts_y - a_ts_y;
ts_d2 = c_ts_y - b_ts_y;
lcd_d = yres-50-50;
g_kx = ((double)(2*lcd_s)) / (ts_s1 + ts_s2);
g_ky = ((double)(2*lcd_d)) / (ts_d1 + ts_d2);
g_ts_xc = e_ts_x;
g_ts_yc = e_ts_y;
g_lcd_xc = xres/2;
g_lcd_yc = yres/2;
我们需要把 + 在LCD上显示出来并且读出数据
void get_calibrate_point_data(int lcd_x, int lcd_y, int *px, int *py)
fb_disp_cross(lcd_x, lcd_y, 0xffffff);
ts_read_raw(px, py);
int is_ts_xy_swap(int a_ts_x, int a_ts_y, int b_ts_x, int b_ts_y)
int dx = b_ts_x - a_ts_x;
int dy = b_ts_y - a_ts_y;
if (dx & 0)
if (dy & 0)
if(dx & dy)
void swap_xy(int *px, int *py)
int tmp = *
void ts_read(int *lcd_x, int *lcd_y)
int ts_x, ts_y;
ts_read_raw(&ts_x, ts_y);
if (g_ts_xy_swap)
swap_xy(&ts_x, &ts_y);
*lcd_x = g_kx * (ts_x - g_ts_xc) + g_lcd_
*lcd_y = g_ky * (ts_y - g_ts_yc) + g_lcd_
接下来我们画线,
我们在framebuffer.c中把清屏函数单独实现
void clear_screen(unsigned int color)
unsigned char *p0;
unsigned short *p;
unsigned int *p2;
if (bpp == 8)
p0 = (unsigned char *)fb_
for (x = 0; x & x++)
for (y = 0; y & y++)
else if (bpp == 16)
p = (unsigned short *)fb_
for (x = 0; x & x++)
for (y = 0; y & y++)
*p++ = convert32bppto16bpp(color);
else if (bpp == 32)
p2 = (unsigned int *)fb_
for (x = 0; x & x++)
for (y = 0; y & y++)
先显示一个一个点,让后显示开始校准提示,
校准完提示 ok draw
打开我们的 touchscreen_test.c文件
void touchscreen_test(void)
unsigned int fb_
int xres, yres,
get_lcd_params(&fb_base, &xres, &yres, &bpp);
touchscreen_init();
clear_screen(0);
fb_print_string(70, 70, "Touc cross to calibrate touchscreen", 0xffffff);
ts_calibrate();
fb_print_string(70, yres - 70, "OK! To draw!", 0xffffff);
ts_read(&x, &y);
printf(" x = %d, y = %d\n\r", x, y);
fb_put_pixel(x, y, 0xff00);
我们添加了 tslib.c需要修改Makefile
添加objs += adc_touchscreen/tslib.o
objs = start.o init.o nand_flash.o led.o uart.o main.o exception.o interrupt.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
objs += adc_touchscreen/tslib.o
all: $(objs)
#arm-linux-ld -Ttext 0 -Tdata 0x
start.o led.o uart.o init.o main.o -o sdram.elf
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
rm -f *.bin $(objs) *.elf *.dis
arm-linux-gcc -march=armv4 -c -o $@ $&
arm-linux-gcc -march=armv4 -c -o $@ $&
第011节触摸屏编程测试
发现程序有bug,点击坐标一次,程序就完成执行,我们需要修改触摸屏文件tsib.c
static double g_
static double g_
static int g_ts_xc, g_ts_
static int g_lcd_xc, g_lcd_
static int g_ts_xy_swap = 0;
static unsigned int fb_
static int xres, yres,
void get_calibrate_point_data(int lcd_x, int lcd_y, int *px, int *py)
fb_disp_cross(lcd_x, lcd_y, 0xffffff);
ts_read_raw(&x, &y, &pressure);
} while (pressure == 0);
ts_read_raw(&x, &y, &pressure);
printf("get raw data: x = %08d, y = %08d\n\r", x, y);
} while (pressure);
printf("return raw data: x = %08d, y = %08d\n\r", *px, *py);
fb_disp_cross(lcd_x, lcd_y, 0);
修改touchcreen.c文件添加压力值相关信息
static int g_ts_
void report_ts_xy(int x, int y, int pressure);
int ts_read(int *lcd_x, int *lcd_y, int *lcd_pressure)
int ts_x, ts_y, ts_
int tmp_x, tmp_y;
ts_read_raw(&ts_x, &ts_y, &ts_pressure);
if (g_ts_xy_swap)
swap_xy(&ts_x, &ts_y);
tmp_x = g_kx * (ts_x - g_ts_xc) + g_lcd_
tmp_y = g_ky * (ts_y - g_ts_yc) + g_lcd_
if (tmp_x & 0 || tmp_x &= xres || tmp_y & 0 || tmp_y &= yres)
return -1;
*lcd_x = tmp_x;
*lcd_y = tmp_y;
*lcd_pressure = ts_
void ts_read_raw(int *px, int *py, int *ppressure)
while (g_ts_data_valid == 0);
*px = g_ts_x;
*py = g_ts_y;
*ppressure = g_ts_
g_ts_data_valid = 0;
void report_ts_xy(int x, int y, int pressure)
if (g_ts_data_valid == 0)
g_ts_pressure =
g_ts_data_valid = 1;
void Isr_Tc(void)
if (ADCDAT0 & (1&&15))
enter_wait_pen_down_mode();
report_ts_xy(0, 0, 0);
enter_auto_measure_mode();
ADCCON |= (1&&0);
void Isr_Adc(void)
int x = ADCDAT0;
int y = ADCDAT1;
static int adc_cnt = 0;
static int adc_x = 0;
static int adc_y = 0;
if (!(x & (1&&15)))
x &= 0x3ff;
y &= 0x3ff;
report_ts_xy(x, y, 1);
ts_timer_enable();
adc_x += (x & 0x3ff);
adc_y += (y & 0x3ff);
adc_cnt++;
if (adc_cnt == 16)
adc_x &&= 4;
adc_y &&= 4;
report_ts_xy(adc_x, adc_y, 1);
adc_cnt = 0;
adc_x = 0;
adc_y = 0;
ts_timer_enable();
enter_auto_measure_mode();
ADCCON |= (1&&0);
adc_cnt = 0;
adc_x = 0;
adc_y = 0;
ts_timer_disable();
enter_wait_pen_down_mode();
report_ts_xy(0, 0, 0);
enter_wait_pen_up_mode();
那么我们的tslib.c中
void get_calibrate_point_data(int lcd_x, int lcd_y, int *px, int *py)
fb_disp_cross(lcd_x, lcd_y, 0xffffff);
ts_read_raw(&x, &y, &pressure);
} while (pressure == 0);
ts_read_raw(&x, &y, &pressure);
printf("get raw data: x = %08d, y = %08d\n\r", x, y);
} while (pressure);
printf("return raw data: x = %08d, y = %08d\n\r", *px, *py);
fb_disp_cross(lcd_x, lcd_y, 0);
int get_lcd_x_frm_ts_x(int ts_x)
return g_kx * (ts_x - g_ts_xc) + g_lcd_
int get_lcd_y_frm_ts_y(int ts_y)
return g_ky * (ts_y - g_ts_yc) + g_lcd_
void ts_calibrate(void)
int a_ts_x, a_ts_y;
int b_ts_x, b_ts_y;
int c_ts_x, c_ts_y;
int d_ts_x, d_ts_y;
int e_ts_x, e_ts_y;
int ts_s1, ts_s2;
int lcd_s;
int ts_d1, ts_d2;
int lcd_d;
get_lcd_params(&fb_base, &xres, &yres, &bpp);
get_calibrate_point_data(50, 50, &a_ts_x, &a_ts_y);
get_calibrate_point_data(xres-50, 50, &b_ts_x, &b_ts_y);
get_calibrate_point_data(xres-50, yres-50, &c_ts_x, &c_ts_y);
get_calibrate_point_data(50, yres-50, &d_ts_x, &d_ts_y);
get_calibrate_point_data(xres/2, yres/2, &e_ts_x, &e_ts_y);
g_ts_xy_swap = is_ts_xy_swap(a_ts_x, a_ts_y, b_ts_x, b_ts_y);
if (g_ts_xy_swap)
swap_xy(&a_ts_x, &a_ts_y);
swap_xy(&b_ts_x, &b_ts_y);
swap_xy(&c_ts_x, &c_ts_y);
swap_xy(&d_ts_x, &d_ts_y);
swap_xy(&e_ts_x, &e_ts_y);
ts_s1 = b_ts_x - a_ts_x;
ts_s2 = c_ts_x - d_ts_x;
lcd_s = xres-50 - 50;
ts_d1 = d_ts_y - a_ts_y;
ts_d2 = c_ts_y - b_ts_y;
lcd_d = yres-50-50;
g_kx = ((double)(2*lcd_s)) / (ts_s1 + ts_s2);
g_ky = ((double)(2*lcd_d)) / (ts_d1 + ts_d2);
g_ts_xc = e_ts_x;
g_ts_yc = e_ts_y;
g_lcd_xc = xres/2;
g_lcd_yc = yres/2;
printf("A lcd_x = %08d, lcd_y = %08d\n\r", get_lcd_x_frm_ts_x(a_ts_x), get_lcd_y_frm_ts_y(a_ts_y));
printf("B lcd_x = %08d, lcd_y = %08d\n\r", get_lcd_x_frm_ts_x(b_ts_x), get_lcd_y_frm_ts_y(b_ts_y));
printf("C lcd_x = %08d, lcd_y = %08d\n\r", get_lcd_x_frm_ts_x(c_ts_x), get_lcd_y_frm_ts_y(c_ts_y));
printf("D lcd_x = %08d, lcd_y = %08d\n\r", get_lcd_x_frm_ts_x(d_ts_x), get_lcd_y_frm_ts_y(d_ts_y));
printf("E lcd_x = %08d, lcd_y = %08d\n\r", get_lcd_x_frm_ts_x(e_ts_x), get_lcd_y_frm_ts_y(e_ts_y));
我们转换出的XY坐标值不是特别稳定
void touchscreen_timer_irq(void)
if (get_status_of_ts_timer() == 0)
if (ADCDAT0 & (1&&15))
ts_timer_disable();
enter_wait_pen_down_mode();
enter_auto_measure_mode();
ADCCON |= (1&&0);
修改touchscreen-test.c文件
void touchscreen_test(void)
unsigned int fb_
int xres, yres,
get_lcd_params(&fb_base, &xres, &yres, &bpp);
touchscreen_init();
clear_screen(0);
fb_print_string(70, 70, "Touc cross to calibrate touchscreen", 0xffffff);
ts_calibrate();
fb_print_string(70, yres - 70, "OK! To draw!", 0xffffff);
if (ts_read(&x, &y, &pressure) == 0)
printf(" x = %d, y = %d\n\r", x, y);
if (pressure)
fb_put_pixel(x, y, 0xff00);
把LCD的电压值,成功转化成屏幕的坐标
对于触摸屏要多次测量,求平均值
要丢弃非法值(以LCD分辨率作为判断标准)
校准时一定要点准
参考tslib库,
第012节触摸屏编程完善
我们触摸屏校准虽然可以正常运行,但是有些问题,比如在触摸屏上点一个点,同时屏幕上面会显示另一个点,我们按住屏幕不动的同时将其转换成LCD坐标并且描点,就表明数值不大稳定
我们第一次点击触摸屏会出现两个点
长按,LCD上的点会越来越大
*根源在于我们得到的LCD坐标值不稳定,根源ADC转换出来的xy坐标值不稳定
我们打开touchscreen.c问题出现在这里面
#include "../s3c2440_soc.h"
#define ADC_INT_BIT (10)
#define TC_INT_BIT
#define INT_ADC_TC
#define WAIT_PEN_DOWN
#define WAIT_PEN_UP
#define YM_ENABLE
#define YM_DISABLE
#define YP_ENABLE
#define YP_DISABLE
#define XM_ENABLE
#define XM_DISABLE
#define XP_ENABLE
#define XP_DISABLE
#define PULLUP_ENABLE
#define PULLUP_DISABLE
#define AUTO_PST
#define WAIT_INT_MODE
#define NO_OPR_MODE
static volatile int g_ts_timer_enable = 0;
static int g_ts_x;
static int g_ts_y;
static int g_ts_
static int g_ts_data_valid = 0;
static int test_x_array[16];
static int test_y_array[16];
void report_ts_xy(int x, int y, int pressure);
void enter_wait_pen_down_mode(void)
ADCTSC = WAIT_PEN_DOWN | PULLUP_ENABLE | YM_ENABLE | YP_DISABLE | XP_DISABLE | XM_DISABLE | WAIT_INT_MODE;
void enter_wait_pen_up_mode(void)
ADCTSC = WAIT_PEN_UP | PULLUP_ENABLE | YM_ENABLE | YP_DISABLE | XP_DISABLE | XM_DISABLE | WAIT_INT_MODE;
void enter_auto_measure_mode(void)
ADCTSC = AUTO_PST | NO_OPR_MODE;
int is_in_auto_mode(void)
return ADCTSC & AUTO_PST;
void Isr_Tc(void)
if (ADCDAT0 & (1&&15))
enter_wait_pen_down_mode();
report_ts_xy(0, 0, 0);
enter_auto_measure_mode();
ADCCON |= (1&&0);
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_
void report_ts_xy(int x, int y, int pressure)
if (g_ts_data_valid == 0)
g_ts_pressure =
g_ts_data_valid = 1;
void ts_read_raw(int *px, int *py, int *ppressure)
while (g_ts_data_valid == 0);
*px = g_ts_x;
*py = g_ts_y;
*ppressure = g_ts_
g_ts_data_valid = 0;
void touchscreen_timer_irq(void)
if (get_status_of_ts_timer() == 0)
if (is_in_auto_mode())
if (ADCDAT0 & (1&&15))
printf("timer set pen down\n\r");
ts_timer_disable();
enter_wait_pen_down_mode();
report_ts_xy(0, 0, 0);
enter_auto_measure_mode();
ADCCON |= (1&&0);
void Isr_Adc(void)
int x = ADCDAT0;
int y = ADCDAT1;
static int adc_cnt = 0;
static int adc_x = 0;
static int adc_y = 0;
enter_wait_pen_up_mode();
if (!(ADCDAT0 & (1&&15)))
x &= 0x3ff;
y &= 0x3ff;
report_ts_xy(x, y, 1);
ts_timer_enable();
adc_x += (x & 0x3ff);
adc_y += (y & 0x3ff);
test_x_array[adc_cnt] = (x & 0x3ff);
test_y_array[adc_cnt] = (y & 0x3ff);
adc_cnt++;
if (adc_cnt == 16)
adc_x &&= 4;
adc_y &&= 4;
report_ts_xy(adc_x, adc_y, 1);
adc_cnt = 0;
adc_x = 0;
adc_y = 0;
enter_wait_pen_up_mode();
ts_timer_enable();
enter_auto_measure_mode();
ADCCON |= (1&&0);
adc_cnt = 0;
adc_x = 0;
adc_y = 0;
printf("adc report pen down\n\r");
ts_timer_disable();
enter_wait_pen_down_mode();
report_ts_xy(0, 0, 0);
void AdcTsIntHandle(int irq)
if (SUBSRCPND & (1&&TC_INT_BIT))
if (SUBSRCPND & (1&&ADC_INT_BIT))
Isr_Adc();
SUBSRCPND = (1&&TC_INT_BIT) | (1&&ADC_INT_BIT);
void adc_ts_int_init(void)
SUBSRCPND = (1&&TC_INT_BIT) | (1&&ADC_INT_BIT);
register_irq(31, AdcTsIntHandle);
INTSUBMSK &= ~((1&&ADC_INT_BIT) | (1&&TC_INT_BIT));
void adc_ts_reg_init(void)
ADCCON = (1&&14) | (49&&6) | (0&&3);
ADCDLY = 60000;
void touchscreen_init(void)
adc_ts_reg_init();
printf("ADCUPDN = 0x%x, SUBSRCPND = 0x%x, SRCPND = 0x%x\n\r", ADCUPDN, SUBSRCPND, SRCPND);
adc_ts_int_init();
register_timer("touchscreen", touchscreen_timer_irq);
enter_wait_pen_down_mode();
void print_test_array(void)
printf("test array x : ");
for (i = 0; i & 16; i++)
printf("%08d ", test_x_array[i]);
printf("\n\r");
printf("test array y : ");
for (i = 0; i & 16; i++)
printf("%08d ", test_y_array[i]);
printf("\n\r");
void ts_read_raw_test(int *px, int *py, int *ppressure)
while (g_ts_data_valid == 0);
*px = g_ts_x;
*py = g_ts_y;
*ppressure = g_ts_
print_test_array();
g_ts_data_valid = 0;
tslib.c使用了ts_read_raw函数
void get_calibrate_point_data(int lcd_x, int lcd_y, int *px, int *py)
int sum_x = 0, sum_y = 0;
int cnt = 0;
fb_disp_cross(lcd_x, lcd_y, 0xffffff);
/* 等待点击 */
//ts_read_raw(&x, &y, &pressure);
//我们使用测试程序去读这些值
ts_read_raw_test(&x, &y, &pressure);
} while (pressure == 0);
if (cnt & 128)
sum_x += x;
sum_y += y;
//ts_read_raw(&x, &y, &pressure);
//ts_read_raw_test(&x, &y, &pressure);
printf("get raw data: x = %08d, y = %08d, cnt = %d\n\r", x, y, cnt);
} while (pressure);
*px = sum_x /
*py = sum_y /
printf("return raw data: x = %08d, y = %08d\n\r", *px, *py);
/* 直到松开才返回 */
fb_disp_cross(lcd_x, lcd_y, 0);
我们来看点击一下是不是得到了距离非常远的两个值
对于同一个点得到的是255 945 945 944
发现 945 944经常出现
我们查一下原因,进入touchscreen.c中
void Isr_Adc(void)
int x = ADCDAT0;
int y = ADCDAT1;
static int adc_cnt = 0;
static int adc_x = 0;
static int adc_y = 0;
enter_wait_pen_up_mode();
if (!(ADCDAT0 & (1&&15)))
x &= 0x3ff;
y &= 0x3ff;
report_ts_xy(x, y, 1);
ts_timer_enable();
adc_x += (x & 0x3ff);
adc_y += (y & 0x3ff);
test_x_array[adc_cnt] = (x & 0x3ff);
test_y_array[adc_cnt] = (y & 0x3ff);
adc_cnt++;
if (adc_cnt == 16)
adc_x &&= 4;
adc_y &&= 4;
report_ts_xy(adc_x, adc_y, 1);
adc_cnt = 0;
adc_x = 0;
adc_y = 0;
enter_wait_pen_up_mode();
ts_timer_enable();
enter_auto_measure_mode();
ADCCON |= (1&&0);
adc_cnt = 0;
adc_x = 0;
adc_y = 0;
printf("adc report pen down\n\r");
ts_timer_disable();
enter_wait_pen_down_mode();
report_ts_xy(0, 0, 0);
发现这些值中还有944,我需要继续查找原因,
在touchscreen.c时钟处理函数中添加打印信息
void touchscreen_timer_irq(void)
if (get_status_of_ts_timer() == 0)
if (is_in_auto_mode())
if (ADCDAT0 & (1&&15))
printf("timer set pen down\n\r");
ts_timer_disable();
enter_wait_pen_down_mode();
report_ts_xy(0, 0, 0);
enter_auto_measure_mode();
ADCCON |= (1&&0);
非常频繁打印timer set pen down
*我们的判断有问题
打开芯片手册,搜索这个寄存器,Bit15确实是判断按下或者松开
只有在中断模式下,这一位才可以正确反应是按下还是松开的状态,修改touchscreen_timer_irq函数
void touchscreen_timer_irq(void)
if (get_status_of_ts_timer() == 0)
if (is_in_auto_mode())
if (ADCDAT0 & (1&&15))
printf("timer set pen down\n\r");
ts_timer_disable();
enter_wait_pen_down_mode();
report_ts_xy(0, 0, 0);
enter_auto_measure_mode();
ADCCON |= (1&&0);
int is_in_auto_mode(void)
return ADCTSC & AUTO_PST;
我们接着实验
发现并没有捕捉到笔的松开模式 ,
修改tslib,取消ts_read_raw_test
重新进行测试.
void get_calibrate_point_data(int lcd_x, int lcd_y, int *px, int *py)
int sum_x = 0, sum_y = 0;
int cnt = 0;
fb_disp_cross(lcd_x, lcd_y, 0xffffff);
/* 等待点击 */
ts_read_raw(&x, &y, &pressure);
//ts_read_raw_test(&x, &y, &pressure);
} while (pressure == 0);
if (cnt & 128)
ts_read_raw(&x, &y, &pressure);
//ts_read_raw_test(&x, &y, &pressure);
printf("get raw data: x = %08d, y = %08d, cnt = %d\n\r", x, y, cnt);
} while (pressure);
*px = sum_x /
*py = sum_y /
printf("return raw data: x = %08d, y = %08d\n\r", *px, *py);
/* 直到松开才返回 */
fb_disp_cross(lcd_x, lcd_y, 0);
现在发现点点不准确
发现校准的值和我们之前的不一样,修改我们的tslib校准程序
void get_calibrate_point_data(int lcd_x, int lcd_y, int *px, int *py)
int sum_x = 0, sum_y = 0;
int cnt = 0;
fb_disp_cross(lcd_x, lcd_y, 0xffffff);
/* 等待点击 */
ts_read_raw(&x, &y, &pressure);
//ts_read_raw_test(&x, &y, &pressure);
} while (pressure == 0);
//我们把求和的次数限制为128次
if (cnt & 128)
sum_x += x;
sum_y += y;
ts_read_raw(&x, &y, &pressure);
//ts_read_raw_test(&x, &y, &pressure);
printf("get raw data: x = %08d, y = %08d, cnt = %d\n\r", x, y, cnt);
} while (pressure);
*px = sum_x /
*py = sum_y /
printf("return raw data: x = %08d, y = %08d\n\r", *px, *py);
/* 直到松开才返回 */
fb_disp_cross(lcd_x, lcd_y, 0);
我们可以参考tslib
使用矩阵进行校准,适用性更强
使用多种方法消除误差,多次测量求平均值
判断相领点的距离,如果突然变化很大,就有可能是错误值
第一期的视频在于裸机基本操作
视频的要点在于
启动ADC时不应该进入等待中断模式,它会影响数据
只有在”等待中断模式”下才可以使用ADCDAT0’BIT 15来判断触摸笔状态
校准非常重要,所以在程序种多次测量求平均值(不仅仅是在adc中断种求平均值)
没有更多推荐了,}

我要回帖

更多关于 小米5x 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信