 鲜花( 0)  鸡蛋( 0)
|
这是嵌入式中Linux系统触摸屏驱动开发。 ]# z% y2 i5 h0 y/ _
1 o, M, o, f; b- v5 d, }
SPI接口的简介
9 E( w; b5 T% B; ?1 @4 z! ]3 W1 [) `9 C
串行外围设备接口SPI总线技术是摩托罗拉公司推出的一种全双工、同步串行接口,它提供了功能强大的四线接口(接收线、传输线、时钟线和从片选线)。 % U- @/ X* E# G
) w: s5 |- O3 U0 T+ @# c0 f
SPI 的从设备和主设备共用一个时钟线,而时钟始终是从主设备里发送出来的。当823e是主模式的时候,片选信号线就停用,如果是从模式的话,它的从片选线低电 平使能。在本例中,823e是主设备,
, ?: R% D1 Y: o% a7 m6 L! w$ T9 v& Q g2 y3 i: s
所以我们另外选用了一个823e的GPIO(通用输入输出口)作为从设备的片选信号。大多数同步串行式数据转换器都 很容易与这种接口连接,其硬件功能很强,所以,与SPI有关的软件就相当简单,使CPU
9 V. l( u2 d3 c0 W* y6 g4 {* d* m% d9 h* K! c
有更多的时间处理其他事务。 . T* R9 j9 y/ j
6 F% j# W$ c& X m5 q/ E. F& W触摸屏的硬件
2 ^0 k, N4 j/ i
8 W( t& \; n w4 S) Y触摸屏输入系统由触摸屏、触摸屏控制芯片和数据处理器三部分组成。触摸屏按其技术原理可分为五类:矢量压力传感式、电阻式、电容式、红外线式和表面声波式,其中电阻式触摸屏在嵌入式系统中
! p- U" E q9 G6 `2 J
/ J) A: S$ a- B3 m: E+ B用的较多。 9 Z- ~8 b+ F. J; Y/ t
, r6 u7 Q. R. j4 p! i* R
我 们选用的触摸屏是AMD公司的电阻式触摸屏AMT 9502。触摸屏控制芯片是TI公司的模数转换芯片ADS7846。该芯片支持SPI通信协议,所以我们就用823e的SPI接口与ADS7846芯片通 信,从触摸屏得到的
% b' B3 q5 V( l! R( b) K! w
; @& f& o( C5 N模拟信号经过模数转换器后输入作为数据处理器的823e。
F( J: B) S9 Z$ T. F0 I: h) f5 M# N" ~& d2 M# I
软件程序 ; y5 H4 N- Q4 k+ _
1 n- \2 h& i- ] U, ~: ]
823e 通过SPI接口与触摸屏控制器通信,所以对触摸屏的控制就是对SPI接口的操作。完成SPI接口驱动的编写之后,就能够与触摸屏控制器建立通信。在 Linux内核运行完毕之后,SPI接口要打开,并且+ w8 x1 g) b8 u1 u6 ^0 |8 _; ~+ u
8 N8 Q/ R) t W$ k( A. E( ]9 {已经分配了一部分内存供它使用。同时,SPI的中断程序已经加入等待队列,一旦SPI接口有中 断,SPI的中断服务程序就被唤醒,开始运行。这部分的工作是在系统启动过程中运行的初始化函数来完成8 z9 w1 C4 m2 d) Z8 k, V$ R$ o
# n+ A+ a9 `. C# j- t4 {的。下面将结合源代码来讨论初始化函数的编写,其 中,就两点进行重点讨论。 ) s \7 ?0 B4 ? u* J, ^# w( x
+ G3 t5 X* @& W$ D/ E7 Y& q- O/ e因 为SCCx的网络参数空间和SPI的参数空间有冲突,如果要在使用SCCx作为网口的同时还使用SPI驱动的话,就要装载microcode,然后重新定 位SPI的参数空间。而micropatch就是装载microcode的一个/ {$ b5 t* |4 `" X
4 y9 X d( G# n% C$ Y
文件,这个文件里的microcode可以到motorola的官方网站上 去下载。
) ~# E& P" H9 ^/ O8 U: u
6 i1 u, p; @8 {5 RCPM包括一部分双向RAM口,称为参数RAM,它包括USB、SCC、SMC、SPI、I2C和IDMA信道操作。其中,SPI和I2C参数区域可以被重新定位到另外的32位的参数区域。仔细阅读完下面的代码,就可以很好的
- k( B- F: }5 a# A/ \: J, T- u3 b0 g+ T* S* U6 u' c
理解这个过程是如何操作的了:
* r1 K/ t5 ~1 W( j" S0 o' n6 H$ b& g, H3 D
spi = (spi_t *)&cp->cp_dparam[PROFF_SPI]; printk(" the spi addr is %p\n",spi); if ((reloc = spi->spi_rpbase)) { spi = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase]; printk(" MICROCODE ' T C5 ?# |( ~9 g3 O6 Y7 i
3 e9 W' G. R# ~8 G& u
RELOCATION PATCH \n"); }
8 k. |! _( p, |% I
: o! @! l% D6 O+ G( m& N上面这一端代码的作用是:首先查询是否已经使用了microcode,然后取得重新定位后的指针(装载microcode和重新定位的操作在microcode.c里完成)。
X7 P/ D6 O u0 T% r4 d* [# H6 R( j
RAM里的SPI描述符 ! a; I1 {# j0 q2 Y
; d3 O7 |! B7 K6 U$ C* y5 j有 关SPI接口的描述符保存在缓冲区里,缓冲区的地址由双向RAM里SPI缓冲区描述符指定。要发送的数据在发送缓冲区里,接收的数据将被存到发送缓冲区 里。缓冲区描述符环路组成一个环路,帮助逐2 K6 P: F9 j* Q" Q- f2 q M9 P7 A
2 [0 A; m4 G( l) g; _' a. d
步传输(接收)想要发送(接收)的数据。正是由于这些缓冲区描述符,通信处理模块才能够完成通信,并且说明并处 理错误。 8 A$ `4 P- ]2 r3 W( O: t6 V, i# v+ S& n
^6 V6 u- h+ f3 R3 p可以通过一段代码来看上面示意图的过程是如何在初始化函数里实现的: 5 H4 d6 z5 `$ Y
z: z+ Z ~( U0 Q5 S0 J$ L# l. v
spi->spi_rbase = r_rbase = dp_addr; spi->spi_tbase = r_tbase = dp_addr + sizeof(cbd_t); /*把RXBD RING的地址写入POINTER TO SPI RX RING 把 TXBD RINT 的地址写入POINTER TO SPI TX 8 X0 J& U' F4 [# ]$ q# }. d
7 C& R+ o6 f {: ~* ^* c `
RING*/ spi->spi_rbptr = spi->spi_rbase; spi->spi_tbptr = spi->spi_tbase; /*以上的两句代码必须得写,否则的话就会在读写/ tbdf = (cbd_t *)&cp->cp_dpmem[r_tbase]; rbdf = (cbd_t *)
) b' r& f( G: o$ }5 p: \+ b Y' L+ V3 E+ |. N' L: p, Q# g
&cp->cp_dpmem[r_rbase]; /*从这句代码里可以看出,RXBDRING的地址是在双向RAM里*/ tbdf->cbd_sc &= ~BD_SC_READY; rbdf->cbd_sc &= ~BD_SC_EMPTY; /*设置RING的状态,发送的RING 设置成非准
3 X; Q. }: O3 B8 _6 D- S4 K2 p
* s9 C+ k3 K i2 E% L. N- N" I) p备发送状态, 接受的RING设置成非准备接受状态*/ rxbuffer = m8xx_cpm_hostalloc(2); txbuffer = m8xx_cpm_hostalloc(2);/*得到两个空间 */ tbdf->cbd_bufaddr = __pa(txbuffer); rbdf-/ a3 c4 v9 N# {/ J# D+ V* ^1 v
" ~6 p% e9 }9 b5 S5 t8 Q! r>cbd_bufaddr = __pa(rxbuffer); /*内存映射;并把DATA POINTER 设置成RX DATA BUFFER 的地址*/
, d V! @0 u, a8 T, M2 b5 ?: w1 p/ K$ M! X# u- q Q' Z
以 上的代码是初始化函数里完成的,一旦初始化函数正确运作,就可以采取正确的步骤进行SPI口通信了。以上初始化完之后,要调用 cpm_install_handler函数,该函数的作用是把中断函数注册进内$ E8 G3 ]1 r! I* a5 \8 I8 x, ^
# r$ D, a& L! ^( E, n1 b
核,一旦SPI口产生硬件中断,就调用中断函数,中断函数的编写可以依据 不同系统的不同需要,在本例中,我们使得一旦调用中断函数,就读取SPI接收到的数据。 : }% H2 t( e' h \% R. I
- _$ m$ ~2 i9 a# l6 m. ?$ o
接下来以如何发送数据为例,分析如何操作SPI口通信。
+ m$ T1 Z, J+ `( V0 G( ], }( ^ F1 k1 A3 [5 B
发送数据的步骤
]; \/ a$ H0 j, d8 a: X, `4 k
D+ f8 u) l. ~4 _在此例中,设SPI接口为主模式。为了开始数据传送过程,内核把要传送的数据写到一个数据缓冲区,然后配置缓冲区描述符,以达到传送的目的。以下给出发送数据的一段代码,通过代码解释传输的过
! P1 a4 j+ v8 j7 o- k2 @: P4 m: L* r) H$ y/ b# m$ `1 g# g5 d
程。
& w$ V8 G' u' d. L7 }
0 K. V. G( G9 X- I. |memset((void*)txbuffer,0,2);/*清空buffer*/ tbdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP; tbdf->cbd_datlen = 2; /*设置发送缓冲区的状态控制寄存器的值和发送数据的个数*/
( T7 x: F3 ^. z
+ ^9 B# E0 z1 `) O$ x+ g; ^rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; rbdf->cbd_datlen = 0; /*由于并不打算接受数据,所以个数为0*/ cp->cp_spmode = 0x777f; cp->cp_spie = 0xff; cp->cp_spim = 0x37; /*设置SPI接- ]8 ]1 J: Q8 ^+ }2 A
* m0 g& S0 E/ U" T7 H9 n
口寄存器的值,以便发送数据,设置SPI接口的 主或从模式必须在发送函数里设置,否则的话,不能发送数据*/ cp->cp_spcom |= 0x80;/*开始发送数据*/ udelay(1000);/*必须得等待,否则的话不能
- ?5 ], z" ^* A, x" K- w
) d8 d# b6 V8 I7 c8 U正确读到缓冲区状态控制寄存器的值*/ if((tbdf->cbd_sc & 0x8000)) printk(" spi write error !"); memset((void*)rxbuffer,0,2);
7 y$ B( q" _" x) L% v' e
$ }' P- N; }* q. T数 据通信过程中,最重要的是时序,正确的时序要通过反复的实验才能得到。图3是在实验过程中得到的逻辑图(安捷伦公司的1672G逻辑分析仪测试结果)。其 中,CS是片选信号,CK是时钟信号,DO
( L+ I: l0 j7 a3 J8 c
/ Y& p" I! a$ S( L( n是823e发送的数据。可以使用逻辑分析仪来阅读得到的数据是否和设备发送的数据一致。正确的通信必须经过长时 间的调试才能够取得。 # q$ X+ D9 s; T7 k
1 E6 d3 X1 ?1 j/ A9 w" @在这强调一下,根据ADS7846的使用手册,驱动程序必须在初始化的时候与ADS7846建立通信。所以,823e首先要向ADS7846发送命令,得到ADS7846的回复后建立通信。驱动程序调用SPI的读写函数来实现对ADS7846的操作。
2 R# ?: R) ^+ W7 D p# d$ r( N转自:http://www.3gtarena.com/ |
|