Como diz o ditado: a madeira do abraço nasce no final; a plataforma de nove andares começa em solo cansado; a jornada de mil milhas começa com um passo. Somente quando a fundação é sólida, podemos fazer as coisas mais facilmente. O mesmo se aplica à programação, e somente entendendo o princípio de funcionamento de cada dispositivo podemos escrever um bom código com eficiência. Portanto, ao aprender a dirigir e programar bare metal, primeiro gosto de passar o princípio de funcionamento do dispositivo.
A partir do princípio básico da tela sensível ao toque, podemos saber que, ao usar a tela sensível ao toque, se o SOC integrar um controlador de tela sensível ao toque, nossa principal tarefa é obter a posição da tela sensível ao toque quando pressionada, ou seja, o valor da coordenada na direção xy. Esse valor pode ser passado através da tensão Após a conversão, o valor da tensão adquirida é convertido usando ADC.
nota: a tela de toque resistiva é essencialmente um divisor de tensão resistivo, e os valores da divisão de tensão de diferentes contatos nas direções x e y são diferentes.
- Para medir a coordenada x, xp é conectado à fonte de ponto de 3,3V XM ao terra, e YP / YM não está conectado à fonte de alimentação.Você pode conhecer a coordenada x medindo a pressão de ponto de Yp.
- Para medir a coordenada y, yp se conecta à fonte de ponto de 3.3V yM ao terra e xP / xM não se conecta à fonte de alimentação.Você pode conhecer a coordenada y medindo a pressão do ponto de xp.
HenQuando a tela de toque não é pressionada, o diagrama de circuito equivalente:
No modo de interrupção de espera: s5 e s4 são fechados Quando um contato é pressionado, a tela de toque é equivalente ao terra. Y_ADC muda de nível alto para baixo, isso pode ser usado como um sinal de disparo de interrupção para notificar a CPU que a tela de toque foi pressionada.
SQuando s1 e s3 são fechados, as coordenadas de x podem ser obtidas coletando a tensão de x_adc (o ponto de divisão de tensão no eixo y) (semelhante ao reostato deslizante). Da mesma forma, quando s2 e s4 são fechados, o ponto de divisão de tensão em y_adc (o ponto de divisão de tensão no eixo x ) A coordenada y pode ser obtida coletando a tensão.
Existem 4 modos de trabalho para a tela de toque no ARM2440:
- Aguardando modo de interrupção : Defina o registro ADCTSC para 0xD3.Neste modo, a tela sensível ao toque está aguardando para ser pressionada.Quando a tela sensível ao toque é pressionada, o potencial Y_ADC muda e uma interrupção INT_TC é gerada. Para o S3C2440, o bit8 pode ser definido para indicar se é necessário aguardar a pena para baixo ou a interrupção da caneta para cima .
- Existem dois modos de espera: aguardar o modo caneta para baixo (quando a tela de toque não foi pressionada) e aguardar o modo caneta para cima (depois que a tela de toque for pressionada)
- Modo de conversão de coordenadas do eixo x / y separado : defina os bits de controle correspondentes (ou seja, o estado fechado de s1-s5) para entrar no modo de conversão de coordenadas x / y. Depois que a conversão do valor da coordenada x é concluída, ela é gravada em ADCDAT0 e, após a conversão da coordenada y, é gravada em ADCDAT1 e, em seguida, a interrupção INT_ADC correspondente é acionada.
- Modo de conversão de coordenadas do eixo x / y automático (contínuo) : defina o registro correspondente. Deixe o controlador da tela de toque entrar no modo de conversão automática de coordenadas do eixo x / y, e o controlador da tela de toque converterá automaticamente os valores das coordenadas xey dos contatos. E escreva para ADCDAT0 e ADCDAT1, respectivamente, e emita a interrupção INT_ADC.
- Modo de conversão normal : quando a tela de toque não é usada, é usada como uma conversão ADC normal. Os dados são armazenados em ADCDAT0.
Portanto, atenção especial deve ser dada ao programar: controle esses interruptores (S1 ~ S5) para definir o modo de trabalho correspondente e, em seguida, obtenha o valor da coordenada através da conversão de tensão ADC.
O controlador da tela de toque e a conversão ADC no ARM2440 são reunidos. XP, XM, YP, YM são usados principalmente na tela de toque. XP / YP são conectados ao polo positivo do eixo X / Y da tela de toque e XM / YM são respectivamente conectados ao polo negativo do eixo X / Y da tela de toque. (Ou seja, de castigo). Quando a tela de toque não é usada, esses pinos podem ser usados como pinos de entrada de sinal analógico ADC comuns.
Pode-se saber pela figura acima que a unidade de controle da tela de toque pode gerar duas interrupções: interrup INT_TC interrupção da tela de toque ② INT_ADC, após a conversão da tensão na direção xy ter êxito, ou seja, a interrupção após a conversão da ADC. Essas duas interrupções compartilham o mesmo número de interrupção no interruptor S3C2440. Portanto, é necessário distinguir entre a interrupção do ADC e a interrupção da tela de toque no programa de serviço de interrupção.
Processo de uso da ADC:
- Defina o registro ADCCON, selecione o canal do sinal de entrada e ajuste o relógio de conversão.
- Defina o registro ADCTSC, usado para definir o uso da função da tela de toque e do modo de trabalho.
- Defina o registro ADCCON para iniciar a conversão A / D. (Existem registros de leitura para iniciar a conversão e definir a conversão ativa do bit1).
- Após a conversão, você pode ler os valores nos registros ADCDAT0 e ADCDAT1 para obter as coordenadas x / y, respectivamente.
Processo de uso da tela de toque:
- Pressione a tela de toque para gerar uma interrupção de toque
- Ative a conversão ADC na interrupção de toque e converta a tensão de xy.
- Depois que a conversão ADC é concluída, uma interrupção ADC é gerada
- Leitura de dados ADCDAT ou tensão xy
- Na interrupção do cronômetro, verifique se a tela de toque ainda está pressionada. (O temporizador é adicionado para suportar a função deslizante)
- Se for pressionado, a função de serviço de interrupção por toque é executada novamente.
- Se você deixar ir, acabou
Estrutura de programação com tela de toque:
#include"../timer.h"
#define INT_ADC (31)
#define INT_ADC_S (10)
#define INT_TC (9)
/*S3C2440中ADCTSC的定义
*UD_SEN : Detect Stylus Up or Down status.0 = Detect Stylus Down Interrupt Signal.1 = Detect Stylus Up Interrupt Signal.
*YM_SEN: YM Switch Enable(0 =0 = YM Output Driver Disable 1 = YM Output Driver Enable )
*YP_SEN:YP Switch Enable(0 = YP Output Driver Enable 1 = YP Output Driver Disable)
*XM_SEN: XM Switch Enable(0 =0 = XM Output Driver Disable 1 = XM Output Driver Enable )
*XP_SEN:XP Switch Enable(0 = XP Output Driver Enable 1 = XP Output Driver Disable)
*PULL_UP :Pull-up Switch Enable(0 = XP Pull-up Enable 1 = XP Pull-up Disable)
****
*AUTO_PST:Automatically sequencing conversion of X-Position and Y-Position
*XY_PST:Manually measurement of X-Position or Y-Position(00 = No operation mode 11 = Waiting for Interrupt Mode).
*/
#define DETECT_UP (1<<8)
#define DETECT_DOWN (0<<8)
#define YM_ENBALE (1<<7)
#define YM_DISENBALE (0<<7)
#define YP_ENBALE (0<<6)
#define YP_DISENBALE (1<<6)
#define XM_ENBALE (1<<5)
#define XM_DISENBALE (0<<5)
#define XP_ENBALE (0<<4)
#define XP_DISENBALE (1<<4)
#define PULL_UP_ENABLE (0<<3)
#define PULL_UP_DISENABLE (1<<3)
#define AUTO_PST (1<<2)
#define NORMAL_XYPST (00)
#define WAIT_XYPST (3)
static int global_ts_enable = 0;
static void ts_disable_timer_handler(void);
static void ts_enable_timer_handler(void);
/*
*设置ADCTSC寄存器,控制s1-s5,进入等待中断状态。(等待按下)
*/
void enter_wait_pendown_mode(void)
{
volatile unsigned int *ADCTSC = (volatile unsigned int*)0x58000004;
*ADCTSC =DETECT_DOWN|PULL_UP_ENABLE|YP_DISENBALE|\
YM_ENBALE|XP_DISENBALE|XM_DISENBALE|WAIT_XYPST;
}
/*
*设置ADCTSC寄存器,控制s1-s5,进入等待中断状态。(等待松开)
*/
void enter_wait_pendup_mode(void)
{
volatile unsigned int *ADCTSC = (volatile unsigned int*)0x58000004;
*ADCTSC =DETECT_UP|PULL_UP_ENABLE|YP_DISENBALE|\
YM_ENBALE|XP_DISENBALE|XM_DISENBALE|WAIT_XYPST;
}
/*
*设置ADCTSC寄存器,进入自动连续转换状态。
*/
void enter_auto_convert_mode(void)
{
volatile unsigned int *ADCTSC = (volatile unsigned int*)0x58000004;
*ADCTSC =AUTO_PST |NORMAL_XYPST;
}
/*
*开始ADC转换
*/
void start_adc_convert(void)
{
volatile unsigned int*ADCCON = (volatile unsigned int*)0x58000000;
*ADCCON |= 1;
}
void irq_touch(void)
{
/*需要辨别是触摸屏按下中断还是松开中断
*ADCUPDN : 用来区分中断产生是松开还是按下
*/
volatile unsigned int *ADCUPDN = (volatile unsigned int*)0x58000014;
volatile unsigned int*ADCCON = (volatile unsigned int*)0x58000000;
volatile unsigned int *ADCTSC = (volatile unsigned int*)0x58000004;
if(*ADCUPDN&(1<<0)){/*按下中断*/
/*开启数据转换功能、触摸屏的模式设置为自动(连续转换)
*让触摸屏控制进入"等待中断"状态,等待触摸屏松开。
*/
puts_("stylus is down\n\r");
enter_auto_convert_mode();
start_adc_convert();
}
if(*ADCUPDN&(1<<1)){/*松开中断*/
puts_("stylus is up\n\r");
/*如果是触摸屏松开中断、则需要让控制器进入"等待中断"状态,可以再次使用触摸屏*/
enter_wait_pendown_mode();
}
*ADCUPDN=0;
}
void irq_adc(void)
{
/*读取转换后的数据*/
volatile unsigned int* ADCDAT0 = (volatile unsigned int*)0x5800000C;
volatile unsigned int* ADCDAT1 = (volatile unsigned int*)0x58000010;
volatile unsigned int x=0,y=0;
x = *ADCDAT0 &0x3ff;
y = *ADCDAT1 &0x3ff;
(*ADCDAT0);
int i;
for(i = 0;i<100;i++);
if(!(*ADCDAT0&(1<<15))){
putHex_(x);
putHex_(y);
puts_("\n\r");
enter_wait_pendup_mode();
ts_enable_timer_handler();
}else{/*松开*/
ts_disable_timer_handler();
enter_wait_pendown_mode();
}
}
/*总中断里面辨别是ADC中断还是触摸屏中断*/
void adcts_irq_handle(int irq)
{
/*由于触摸屏按下中断和ADC转换结束完毕中断共用同一个中断号
*所以执行中断的时候需要先分辨是什么中断然后执行相应的中断服务函数
*/
unsigned int *SUBSRCPND = (unsigned int *)0X4A000018;
unsigned int subsrcirq = *SUBSRCPND;
//puts_("this is touch screen interrupt\n\r");
if(subsrcirq&(1<<INT_TC)){
/*如果是触摸屏按下中断,进入此服务函数*/
irq_touch();
}else if(subsrcirq&(1<<INT_ADC_S)){
/*如果是中断转换完成中断,进入此服务函数*/
irq_adc();
/*ADC读取转换数据成功后,在此进入等待中断状态(等待松触摸屏松开)*/
enter_wait_pendup_mode();
}
*SUBSRCPND = subsrcirq;
}
void init_adcts_irq(void)
{
/*开启ADC和触摸屏中断
*禁止中断屏蔽。让中断控制器可以把中断发送
*禁止子中断屏蔽,让产生的中断可以发送到中断控制器处
*/
register_irq(INT_ADC,adcts_irq_handle);
enbale_subsrc_irq(INT_ADC_S);
enbale_subsrc_irq(INT_TC);
}
init_touchscreen_register(void)
{
/*设置ADC的时钟
*(主要是有预分频系数和使能、选择转换通道(触摸屏使用不上)、模式的选择)
*/
volatile unsigned int *ADCCON = (volatile unsigned int *)0x58000000;
*ADCCON = (1<<14) |(49<<6);
}
/*
*开启定时器中断服务函数中的触摸屏定时器功能
*/
static void ts_enable_timer_handler(void)
{
global_ts_enable = 1;
}
/*
*关闭定时器中断服务函数中的触摸屏定时器功能
*/
static void ts_disable_timer_handler(void)
{
global_ts_enable = 0;
}
/*获取定时器的使能位
*/
static int get_status_of_ts_timer(void)
{
return global_ts_enable;
}
/*
*定时器TIMER0中断服务函数,每10ns执行一次
*/
void touch_timer_handler(void)
{
volatile unsigned int* ADCDAT0 = (volatile unsigned int*)0x5800000C;
/*判断触摸屏是否仍然处于按下状态,要是处于按下状态,进入自动转换模式,启动ADC再次转换*/
if(!get_status_of_ts_timer()){/*刚上电还没有启动触摸屏时,不需要进行处理*/
return ;
}
//putHex_(*ADCDAT0);
//puts_("\n\r");
if(!(*ADCDAT0&(1<<15))){/*触摸屏仍然按下,继续进入自动转换和开始ADC转换*/
enter_auto_convert_mode();
start_adc_convert();
}else{/*要是已经松开,关闭定时器转换,则进入"等待中断模式",等待触摸屏再次按下.可以执行后续的处理*/
ts_disable_timer_handler();
enter_wait_pendown_mode();
}
*ADCDAT0 = 0;
}
void init_touch_screen(void)
{
/*开启INT_ADC/INT_TC中断*/
init_adcts_irq();
/*初始化触摸屏控制器*/
init_touchscreen_register();
/*注册定时器中断,用来现实长按和滑动之类的*/
register_timer_handler("touch", touch_timer_handler);
/*进入中断等待模式*/
enter_wait_pendown_mode();
}
Pontos importantes para a programação:
- Como a interrupção da conversão ADC e a tela sensível ao toque compartilham o mesmo número de interrupção, é necessário distinguir a fonte da interrupção.
- Existem dois tipos de interrupções na tela de toque: pressionar e soltar, para que o modo de interrupção de espera precise ser dividido em cima e para baixo (pode ser distinguido pelo registro ADCUPDN, ele precisa ser limpo após cada leitura)
- Para dar um toque longo ou pressionado, você precisa adicionar um timer. Na função de serviço de interrupção do timer, verifique o estado da tela sensível ao toque, se ainda estiver pressionada, continue a conversão ADC. Caso contrário, entre no modo de interrupção de espera.
- Na função de serviço de interrupção do ADC, é necessário adicionar um pequeno atraso. Caso contrário, haverá problemas na leitura do bit mais alto de dados no DATA0. Não sei se é um problema de chip ou qual lógica do meu próprio código não foi tratada. Amigos que querem saber podem dar dicas