dirty 发表于 2024-6-13 23:40

【兆易GD32H759I-EVAL】--13.LCD触摸功能

<div class='showpostmsg'> 本帖最后由 dirty 于 2024-6-14 09:10 编辑

<p>&nbsp; &nbsp; &nbsp; 前面展示了LVGL的移植与界面显示,本篇承接其上讲述LCD触摸功能。</p>

<p><strong><span style="color:#0000ff;">一.原理了解</span></strong></p>

<p>&nbsp; &nbsp; &nbsp; 开发板LCD接口带有触摸引脚如下图,显示屏上带有触摸芯片 XPT2046,通过SPI接口与主控制器通信。</p>

<div style="text-align: center;"></div>

<div style="text-align: center;">图1:触摸原理引脚</div>

<p>&nbsp;</p>

<p>引脚定义</p>

<p>LCD_Touch_PENIRQ-----------PG3&nbsp; &nbsp; &nbsp; 笔触中断信号。当触摸屏被按下,该引脚被拉为低电平。<br />
LCD_SPI4_MOSI----------------PF9&nbsp; &nbsp; &nbsp; &nbsp;SPI串行数据输出</p>

<p>LCD_SPI4_MISO----------------PH7&nbsp; &nbsp; &nbsp;&nbsp;SPI串行数据输入<br />
LCD_SPI4_SCK-----------------PH6&nbsp; &nbsp; &nbsp;&nbsp;SPI时钟<br />
LCD_SPI4_NSS-----------------PF6&nbsp; &nbsp; &nbsp; 片选。拉低输入输出数据有效<br />
LCD_PWM_BackLight---------PG13&nbsp; &nbsp;背光控制<br />
LCD_Touch_Busy---------------PF8&nbsp; &nbsp; &nbsp;忙输出信号</p>

<p>&nbsp;</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>&nbsp;</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 &lt; 8; i++)
        {
                buf = ((d &gt;&gt; (7-i)) &amp; 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 &lt; 12; i++)
        {
                buf = buf &lt;&lt; 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&lt;8;i++)
        {
                temp+=touch_ad_x_get();
                spi_delay(1000);
        }
        temp&gt;&gt;=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&lt;8;i++)
        {
                temp+=touch_ad_y_get();
                spi_delay(1000);
        }
        temp&gt;&gt;=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 &lt;= 0 || sx &gt; 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 &lt;= 0 || sy &gt; 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 &lt; 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 &lt; FILTER_READ_TIMES - 1; i++)
        {
                for(j = i + 1; j &lt; FILTER_READ_TIMES; j++)
                {
                        if(buf &gt; buf)
                        {
                                temp = buf;
                                buf = buf;
                                buf = temp;
                        }
                }
        }
        sum = 0;
        for(i = FILTER_LOST_VAL; i &lt; 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) &gt; AD_ERR_RANGE) || (abs(ad_y1 - ad_y2) &gt; 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(&amp;touch_ad_x, &amp;touch_ad_y))&amp;&amp; (invalid_count &lt;20))
                {
                        invalid_count++;
                }
                if(invalid_count &gt;= 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>&nbsp;</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>&nbsp;</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(&amp;last_x, &amp;last_y);
      data-&gt;state = LV_INDEV_STATE_PR;
    }
    else {
      data-&gt;state = LV_INDEV_STATE_REL;
    }

    /*Set the last pressed coordinates*/
    data-&gt;point.x = last_x;
    data-&gt;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>&nbsp; &nbsp; &nbsp; 经过这几步lvgl 触摸移植完成。</p>

<p>&nbsp;</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&gt;=_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(&amp;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>&nbsp;</p>

<p><strong><span style="color:#0000ff;">三.测验</span></strong></p>

<p>&nbsp; &nbsp; &nbsp; 编译烧录后,屏幕中间显示黄色按钮如下图,触摸后变色及附带数值循环递变,如下视屏。</p>

<div style="text-align: center;"></div>

<div style="text-align: center;">图3:触摸按钮</div>

<p>&nbsp; &nbsp; &nbsp; 至此实现LCD屏触摸功能。</p>

<p>&nbsp;</p>

<p>a97d98a59d5a731fe596821c35c3e52d<br />
&nbsp;</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:29

本帖最后由 dirty 于 2024-6-24 23:40 编辑

<p><strong><span style="color:#e74c3c;">LCD触摸工程代码</span></strong></p>

<p>&nbsp;</p>

lugl4313820 发表于 2024-6-14 11:38

看起来效果挺好的了,我原来就是没有把触摸搞定。

dirty 发表于 2024-6-15 09:35

lugl4313820 发表于 2024-6-14 11:38
看起来效果挺好的了,我原来就是没有把触摸搞定。

<p>一步步来,花点时间,可以搞定的</p>

御坂10032号 发表于 2024-8-11 16:00

<p>X轴有点偏移,怎么校准下。 大概偏移了30</p>

御坂10032号 发表于 2024-8-11 16:03

<p>我Sy+=了30</p>
页: [1]
查看完整版本: 【兆易GD32H759I-EVAL】--13.LCD触摸功能