【兆易GD32H759I-EVAL】--13.LCD触摸功能
<div class='showpostmsg'> 本帖最后由 dirty 于 2024-6-14 09:10 编辑<p> 前面展示了LVGL的移植与界面显示,本篇承接其上讲述LCD触摸功能。</p>
<p><strong><span style="color:#0000ff;">一.原理了解</span></strong></p>
<p> 开发板LCD接口带有触摸引脚如下图,显示屏上带有触摸芯片 XPT2046,通过SPI接口与主控制器通信。</p>
<div style="text-align: center;"></div>
<div style="text-align: center;">图1:触摸原理引脚</div>
<p> </p>
<p>引脚定义</p>
<p>LCD_Touch_PENIRQ-----------PG3 笔触中断信号。当触摸屏被按下,该引脚被拉为低电平。<br />
LCD_SPI4_MOSI----------------PF9 SPI串行数据输出</p>
<p>LCD_SPI4_MISO----------------PH7 SPI串行数据输入<br />
LCD_SPI4_SCK-----------------PH6 SPI时钟<br />
LCD_SPI4_NSS-----------------PF6 片选。拉低输入输出数据有效<br />
LCD_PWM_BackLight---------PG13 背光控制<br />
LCD_Touch_Busy---------------PF8 忙输出信号</p>
<p> </p>
<p><strong><span style="color:#0000ff;">二.代码准备</span></strong></p>
<p>1.触摸引脚初始化。</p>
<pre>
<code>/* SPI SCK pin */
#define SPI_SCK_PIN GPIO_PIN_6
#define SPI_SCK_PORT GPIOH
#define SPI_SCK_LOW() gpio_bit_reset(SPI_SCK_PORT, SPI_SCK_PIN)
#define SPI_SCK_HIGH() gpio_bit_set(SPI_SCK_PORT, SPI_SCK_PIN)
/* SPI MOSI pin */
#define SPI_MOSI_PIN GPIO_PIN_9
#define SPI_MOSI_PORT GPIOF
#define SPI_MOSI_LOW() gpio_bit_reset(SPI_MOSI_PORT, SPI_MOSI_PIN)
#define SPI_MOSI_HIGH() gpio_bit_set(SPI_MOSI_PORT, SPI_MOSI_PIN)
/* SPI MISO pin */
#define SPI_MISO_PIN GPIO_PIN_7
#define SPI_MISO_PORT GPIOH
#define SPI_MISO_READ() gpio_input_bit_get(SPI_MISO_PORT, SPI_MISO_PIN)
/* SPI Chip select pin */
#define SPI_TOUCH_CS_PIN GPIO_PIN_6
#define SPI_TOUCH_CS_PORT GPIOF
#define SPI_TOUCH_CS_LOW() gpio_bit_reset(SPI_TOUCH_CS_PORT,SPI_TOUCH_CS_PIN)
#define SPI_TOUCH_CS_HIGH() gpio_bit_set(SPI_TOUCH_CS_PORT,SPI_TOUCH_CS_PIN)
/* LCD touch interrupt request pin */
#define TOUCH_PEN_INT_PIN GPIO_PIN_3
#define TOUCH_PEN_INT_PORT GPIOG
#define TOUCH_PEN_INT_READ() gpio_input_bit_get(TOUCH_PEN_INT_PORT,TOUCH_PEN_INT_PIN)
void touch_panel_gpio_configure(void)
{
/* GPIO clock enable */
rcu_periph_clock_enable(RCU_GPIOG);
rcu_periph_clock_enable(RCU_GPIOH);
rcu_periph_clock_enable(RCU_GPIOF);
gpio_af_set(SPI_SCK_PORT, GPIO_AF_5, SPI_SCK_PIN);
gpio_mode_set(SPI_SCK_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,SPI_SCK_PIN);
gpio_output_options_set(SPI_SCK_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ,SPI_SCK_PIN);
gpio_af_set(SPI_MOSI_PORT, GPIO_AF_5, SPI_MOSI_PIN);
gpio_mode_set(SPI_MOSI_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,SPI_MOSI_PIN);
gpio_output_options_set(SPI_MOSI_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ,SPI_MOSI_PIN);
gpio_af_set(SPI_MISO_PORT, GPIO_AF_5, SPI_MISO_PIN);
gpio_mode_set(SPI_MISO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE,SPI_MISO_PIN);
gpio_output_options_set(SPI_MISO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ,SPI_MISO_PIN);
gpio_mode_set(SPI_TOUCH_CS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,SPI_TOUCH_CS_PIN);
gpio_output_options_set(SPI_TOUCH_CS_PORT, GPIO_OTYPE_PP,GPIO_OSPEED_60MHZ, SPI_TOUCH_CS_PIN);
/* touch pen IRQ pin PI3 configure */
gpio_mode_set(TOUCH_PEN_INT_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE,TOUCH_PEN_INT_PIN);
gpio_output_options_set(TOUCH_PEN_INT_PORT, GPIO_OTYPE_PP,GPIO_OSPEED_60MHZ, TOUCH_PEN_INT_PIN);
/* set chip select pin high */
SPI_TOUCH_CS_HIGH();
}</code></pre>
<p> </p>
<p>2.触摸驱动。这部分驱动会被用在LVGL 关于触摸功能lv_port_indev.c里。</p>
<p>(1)触摸开始,写数据到触摸屏,读触摸ad值</p>
<pre>
<code>/*!
\brief touch start
\param none
\param none
\retval none
*/
void touch_start(void)
{
spi_clk(0);
spi_cs(1);
spi_mosi(1);
spi_clk(1);
spi_cs(0);
}
/*!
\brief write data to touch screen
\param d: the data to be written
\param none
\retval none
*/
void touch_write(uint8_t d)
{
uint8_t buf, i ;
spi_clk(0);
for( i = 0; i < 8; i++)
{
buf = ((d >> (7-i)) & 0x1);
spi_mosi(buf);
spi_clk(0);
spi_clk(1);
spi_clk(0);
}
}
/*!
\brief read the touch AD value
\param None
\param none
\retval the value of touch AD
*/
uint16_t touch_read(void)
{
uint16_t buf ;
uint8_t i ;
buf=0;
for(i = 0; i < 12; i++)
{
buf = buf << 1 ;
spi_clk(1);
spi_clk(0);
if(RESET != spi_miso())
{
buf = buf + 1 ;
}
}
return( buf );
}</code></pre>
<p>(2)获取触摸X轴、Y轴坐标及防抖平均值</p>
<pre>
<code>/*!
\brief get the AD sample value of touch location at X coordinate
\param none
\param none
\retval channel X+ AD sample value
*/
uint16_t touch_ad_x_get(void)
{
if (RESET != touch_pen_irq())
{
/* touch pen is inactive */
return 0;
}
touch_start();
touch_write(0x00);
touch_write(CH_X);
return (touch_read());
}
/*!
\brief get the AD sample value of touch location at Y coordinate
\param none
\param none
\retval channel Y+ AD sample value
*/
uint16_t touch_ad_y_get(void)
{
if (RESET != touch_pen_irq())
{
/* touch pen is inactive */
return 0;
}
touch_start();
touch_write(0x00);
touch_write(CH_Y);
return (touch_read());
}
/*!
\brief get channel X+ AD average sample value
\param none
\param none
\retval channel X+ AD average sample value
*/
uint16_t touch_average_ad_x_get(void)
{
uint8_t i;
uint16_t temp=0;
for (i=0;i<8;i++)
{
temp+=touch_ad_x_get();
spi_delay(1000);
}
temp>>=3;
return temp;
}
/*!
\brief get channel Y+ AD average sample value
\param none
\param none
\retval channel Y+ AD average sample value
*/
uint16_t touch_average_ad_y_get(void)
{
uint8_t i;
uint16_t temp=0;
for (i=0;i<8;i++)
{
temp+=touch_ad_y_get();
spi_delay(1000);
}
temp>>=3;
return temp;
}
</code></pre>
<p>(3)获取映射到屏幕上的像素点X轴、Y轴坐标及数据过滤</p>
<pre>
<code>/*!
\brief get X coordinate value of touch point on LCD screen
\param adx : channel X+ AD average sample value
\param none
\retval X coordinate value of touch point
*/
uint16_t touch_coordinate_x_get(uint16_t adx)
{
uint16_t sx = 0;
uint32_t
r = adx - AD_Left;
r *= LCD_X - 1;
sx = r / (AD_Right - AD_Left);
if (sx <= 0 || sx > LCD_X)
{
return 0;
}
return sx;
}
/*!
\brief get Y coordinate value of touch point on LCD screen
\param ady : channel Y+ AD average sample value
\param none
\retval Y coordinate value of touch point
*/
uint16_t touch_coordinate_y_get(uint16_t ady)
{
uint16_t sy = 0;
uint32_t
r = ady - AD_Top;
r *= LCD_Y - 1;
sy = r / (AD_Bottom - AD_Top);
if (sy <= 0 || sy > LCD_Y)
{
return 0;
}
return sy;
}
/*!
\brief get a value (X or Y) for several times. Order these values,
remove the lowest and highest and obtain the average value
\param channel_select: select channel X or Y
\arg CH_X: channel X
\arg CH_Y: channel Y
\param none
\retval a value(X or Y) of touch point
*/
uint16_t touch_data_filter(uint8_t channel_select)
{
uint16_t i=0, j=0;
uint16_t buf;
uint16_t sum=0;
uint16_t temp=0;
/* Read data in FILTER_READ_TIMES times */
for(i=0; i < FILTER_READ_TIMES; i++)
{
if (CH_X == channel_select)
{
buf = touch_ad_x_get();
}
else
{
/* CH_Y == channel_select */
buf = touch_ad_y_get();
}
}
/* Sort in ascending sequence */
for(i = 0; i < FILTER_READ_TIMES - 1; i++)
{
for(j = i + 1; j < FILTER_READ_TIMES; j++)
{
if(buf > buf)
{
temp = buf;
buf = buf;
buf = temp;
}
}
}
sum = 0;
for(i = FILTER_LOST_VAL; i < FILTER_READ_TIMES - FILTER_LOST_VAL; i++)
{
sum += buf;
}
temp = sum / (FILTER_READ_TIMES - 2 * FILTER_LOST_VAL);
return temp;
}</code></pre>
<p>(4)获取过滤处理后触摸位置</p>
<pre>
<code>
/*
\brief get the AD sample value of touch location.
get the sample value for several times,order these values,remove the lowest and
highest and obtain the average value
\param channel_select: select channel X or Y
\param none
\arg ad_x: channel X AD sample value
\arg ad_y: channel Y AD sample value
\retval ErrStatus: SUCCESS or ERROR
*/
ErrStatus touch_ad_xy_get(uint16_t *ad_x, uint16_t *ad_y)
{
uint16_t ad_x1=0, ad_y1=0, ad_x2=0, ad_y2=0;
ad_x1 = touch_data_filter(CH_X);
ad_y1 = touch_data_filter(CH_Y);
ad_x2 = touch_data_filter(CH_X);
ad_y2 = touch_data_filter(CH_Y);
if((abs(ad_x1 - ad_x2) > AD_ERR_RANGE) || (abs(ad_y1 - ad_y2) > AD_ERR_RANGE))
{
return ERROR;
}
*ad_x = (ad_x1 + ad_x2) / 2;
*ad_y = (ad_y1 + ad_y2) / 2;
return SUCCESS;
}</code></pre>
<p>(5)触摸扫描。用来探测触摸事件</p>
<pre>
<code>
/*!
\brief detect the touch event
\param none
\param none
\retval ErrStatus: SUCCESS or ERROR
*/
ErrStatus touch_scan(void)
{
uint8_t invalid_count = 0;
if (RESET == touch_pen_irq())
{
/* touch pen is active */
while((SUCCESS != touch_ad_xy_get(&touch_ad_x, &touch_ad_y))&& (invalid_count <20))
{
invalid_count++;
}
if(invalid_count >= 20)
{
touch_ad_x = 0;
touch_ad_y = 0;
return ERROR;
}
}
else
{
touch_ad_x = 0;
touch_ad_y = 0;
return ERROR;
}
return SUCCESS;
}</code></pre>
<p> </p>
<p>3.lvgl触摸功能源文件lv_port_indev.c代码移植</p>
<p>(1)lv_port_indev_init屏蔽除触摸功能外的外设,如下图。这里注册了触摸读函数touchpad_read。</p>
<div style="text-align: center;">
<div style="text-align: center;"></div>
<p> </p>
</div>
<div style="text-align: center;">图2:触摸外设裁剪</div>
<p>(2)回调触摸读函数实现。这里调用了touch_scan触摸扫描函数。</p>
<pre>
<code>/*Will be called by the library to read the touchpad*/
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
static lv_coord_t last_x = 0;
static lv_coord_t last_y = 0;
/*Save the pressed coordinates and the state*/
if(touch_scan())
{
touchpad_get_xy(&last_x, &last_y);
data->state = LV_INDEV_STATE_PR;
}
else {
data->state = LV_INDEV_STATE_REL;
}
/*Set the last pressed coordinates*/
data->point.x = last_x;
data->point.y = last_y;
}</code></pre>
<p>(3)获取触摸的X、Y轴坐标</p>
<pre>
<code>/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
/*Your code comes here*/
// (*x) = 0;
// (*y) = 0;
(*x) = touch_coordinate_x_get(touch_ad_x);
(*y) = LCD_Y - touch_coordinate_y_get(touch_ad_y);
}</code></pre>
<p> 经过这几步lvgl 触摸移植完成。</p>
<p> </p>
<p>4.设计触摸按钮居中,实现触摸后按钮颜色及数值循环递变。</p>
<pre>
<code>static void btn_event_cb(lv_event_t* e)
{
static uint8_t cnt = 0;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t* btn = lv_event_get_target(e);
if (code == LV_EVENT_CLICKED)
{
cnt++;
lv_obj_t* label = lv_obj_get_child(btn, 0);
if(cnt>=_LV_PALETTE_LAST)
{
cnt=0;
}
lv_label_set_text_fmt(label, "Button: %d", cnt);
lv_obj_set_style_bg_color(btn, lv_palette_main(cnt) ,0);//设置背景颜色
}
}
static void lv_example(void)
{
lv_obj_t* btn = lv_btn_create(lv_scr_act());
lv_obj_set_pos(btn, 180, 111);//10,10
lv_obj_set_size(btn, 120, 50);
lv_obj_set_style_bg_color(btn, lv_palette_main(LV_PALETTE_YELLOW) ,0);//设置背景颜色
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);
lv_obj_t* label = lv_label_create(btn);
lv_label_set_text(label, "Button");
lv_obj_center(label);
}
</code></pre>
<p>5.main函数如下</p>
<pre>
<code>/*!
\brief main function
\paramnone
\param none
\retval none
*/
int main(void)
{
ov2640_id_struct ov2640id;
BaseType_t ret;
/* enable the CPU cache */
cache_enable();
/* initialize the LEDs */
test_status_led_init();
/* configure systick */
systick_config();
/* flash the LEDs for 2 time */
led_flash(2);
/* configure USART0 */
usart_config();
/* configure TAMPER key */
gd_eval_key_init(KEY_TAMPER, KEY_MODE_EXTI);
/* output a message on hyperterminal using printf function */
printf("\r\n =================LVGL ================= \r\n");
/**********************LCD Application**********************/
/* config the EXMC access mode */
exmc_synchronous_dynamic_ram_init(EXMC_SDRAM_DEVICE0);
printf("\r\nSDRAM Init \r\n");
/* camera initialization */
dci_ov2640_init();
dci_ov2640_id_read(&ov2640id);
nvic_configuration();
/* DMA interrupt and channel enable */
dma_interrupt_enable(DMA1, DMA_CH7, DMA_CHXCTL_FTFIE);
dma_channel_enable(DMA1, DMA_CH7);
/* DCI enable */
dci_enable();
dci_capture_enable();
delay_1ms(100);
dma_interrupt_disable(DMA1, DMA_CH7, DMA_CHXCTL_FTFIE);
dma_channel_disable(DMA1, DMA_CH7);
dci_capture_disable();
timer_config();
lcd_config();
lcd_init();
/* configure the GPIO of SPI touch panel */
touch_panel_gpio_configure();
#if (LVGL_DMA)
delay_1ms(50);
dma_config();
delay_1ms(1000);
#endif
lv_init();
lv_port_disp_init();
lv_port_indev_init();
lv_example();
// lv_demo_music();
//lv_demo_widgets();
while(1)
{
delay_1ms(5);
lv_task_handler();
}
}
</code></pre>
<p> </p>
<p><strong><span style="color:#0000ff;">三.测验</span></strong></p>
<p> 编译烧录后,屏幕中间显示黄色按钮如下图,触摸后变色及附带数值循环递变,如下视屏。</p>
<div style="text-align: center;"></div>
<div style="text-align: center;">图3:触摸按钮</div>
<p> 至此实现LCD屏触摸功能。</p>
<p> </p>
<p>a97d98a59d5a731fe596821c35c3e52d<br />
</p>
</div><script> var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;" style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
if(parseInt(discuz_uid)==0){
(function($){
var postHeight = getTextHeight(400);
$(".showpostmsg").html($(".showpostmsg").html());
$(".showpostmsg").after(loginstr);
$(".showpostmsg").css({height:postHeight,overflow:"hidden"});
})(jQuery);
} </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script> 本帖最后由 dirty 于 2024-6-24 23:40 编辑
<p><strong><span style="color:#e74c3c;">LCD触摸工程代码</span></strong></p>
<p> </p>
看起来效果挺好的了,我原来就是没有把触摸搞定。 lugl4313820 发表于 2024-6-14 11:38
看起来效果挺好的了,我原来就是没有把触摸搞定。
<p>一步步来,花点时间,可以搞定的</p>
<p>X轴有点偏移,怎么校准下。 大概偏移了30</p>
<p>我Sy+=了30</p>
页:
[1]