prettyprint

2022年12月26日 星期一

[Raspberry Pi Pico (c-sdk)] PIO I2S audio player

 本實驗整合

  1. [Raspberry Pi Pico (c-sdk)] Display: Ep 2 : PIO TFT LCD ILI9341 8-bit parallel C-code Driver
  2. [Raspberry Pi Pico (c-sdk)] Storage: Ep 3. Using SD/MMC Devices and Flash Devices Together
  3. Raspberry Pi Pico PIO(Programmable IO) Episode 4: I2S audio (Play WAVE File In SDCard from MAX98357A DAC)
再加入XPT2046 touch screen模組,製作一台聲音播放器。
各模組接到Raspberry Pi Pico接線腳位如下圖所示:


XPT2046 touch screen controller:

使用SPI界面,連接到SPI0。XPT2046 T_PEN腳位,平時為HIGH,按下時轉為LOW。使用GPIO EDGE_FALL 產生Interrupt。讀取觸控位置坐標,以便執行相關動作。

讀取X與Y座標的指令碼,分別為0xD0與0x90。透過spi送出指令後接著讀取讀取2 Bytes座標值。
連續取樣16次後平均後,再對應到實際TFT螢幕座標。

詳細程式碼如文末所附。

成果影片:


程式碼:



xpt2046.h
#ifndef _XPT2046_H_
#define _XPT2046_H_
#include "pico/stdlib.h"

#define XPT2046_PEN_GPIO    26
#define XPT2046_MOSI        19
#define XPT2046_MISO        16
#define XPT2046_CS          17
#define XPT2046_CLK         18
#define XPT2046_SPI         spi0

#define XPT2046_MIN_RAW_X 2000
#define XPT2046_MAX_RAW_X 30000
#define XPT2046_MIN_RAW_Y 1500
#define XPT2046_MAX_RAW_Y 29000

/* user define actions begin*/
enum {
    ACTION_FILE=1,
    ACTION_PLAY_PAUSE,
    ACTION_STOP,
    ACTION_IDLE
};

extern uint8_t user_action;
extern uint8_t selected_filename[60];
void main_menu();
/* user define actions end*/

void xpt2046_init();
bool xpt2046_getXY(uint16_t *x, uint16_t *y);
#endif

xpt2046.c
 #include "xpt2046.h"
#include "hardware/spi.h"
#include "hardware/gpio.h"
#include "pico/stdlib.h"
#include "stdio.h"
#include "ili9341.h"
#include "ff.h"
#include "spi_sdmmc.h"
#include "fonts/font_fixedsys_mono_24.h"
#include "i2s_pio_dma.h"
#include "string.h"
#include "fonts/play.h"
#include "fonts/stop.h"
#include "fonts/pause.h"
#include "fonts/resume.h"

uint32_t xpt2046_event=0;
uint8_t READ_X = 0xD0;
uint8_t READ_Y = 0x90;

static uint8_t top_index=0;
static uint16_t total_files=0;

/* user define action function*/
uint8_t user_action;            //export
uint8_t selected_filename[60];  // export

static int selected_item=-1;
static uint16_t sx=5, sy = 4;
static uint16_t item_height=30;

static uint8_t audio_files[30][30];
/* action region */
static uint16_t text_rgn_x0=2,text_rgn_x1=216,text_rgn_y0=2,text_rgn_y1=273;
static uint16_t menu_rgn_x0=2,menu_rgn_x1=238,menu_rgn_y0=275,menu_rgn_y1=318;
static uint16_t play_rgn_x0=8,play_rgn_x1=48,play_rgn_y0=276,play_rgn_y1=318;
static uint16_t stop_rgn_x0=100,stop_rgn_x1=140,stop_rgn_y0=276,stop_rgn_y1=318;
static uint16_t su_rgn_x0=220,su_rgn_x1=238,su_rgn_y0=5,su_rgn_y1=25;
static uint16_t sd_rgn_x0=220,sd_rgn_x1=238,sd_rgn_y0=262,sd_rgn_y1=272;

void draw_menu_item() {
    ili9341_fill_rect(2,2,215, 272,0x0000);
    for (int i = 0; i < 9; i++) {
        if (i+top_index < total_files)
            ili9341_draw_string(sx, sy+i*item_height,audio_files[i+top_index], 0xffff, &font_fixedsys_mono_24);
    }
}
void main_menu() {
    FRESULT fr;
    FIL file;
    FATFS fs;
    DIR dir;
    FILINFO finfo;
    uint8_t findex=0;
    

    fr = f_opendir(&dir, SDMMC_PATH"/");
    if (fr != FR_OK) {
        printf("open dir error\n");
        return;
    }
    uint8_t bc;
    fr = f_readdir(&dir, &finfo);
    total_files = 0;
    top_index = 0;
    while (fr == FR_OK && strlen(finfo.fname) > 0) { 
        
        if ((uint8_t)(finfo.fname[0]) != '.') {
            strcpy(audio_files[findex++], finfo.fname);
            total_files++;
       }   
        
        fr = f_readdir(&dir, &finfo);
    }

    ili9341_fill_rect(0,0, TFT_WIDTH, TFT_HEIGHT, 0x0000);
    ili9341_draw_rect(1,1,238, 318,0xffff);
    ili9341_draw_line(217,1,217,274,0xffff);
    ili9341_draw_line(1,274,238,274,0xffff);
    
    ili9341_draw_bitmap(10,277, &play);
    ili9341_draw_bitmap(102, 277, &stop);

    ili9341_fill_rect(222, 5, 10,10, 0xffff);
    ili9341_fill_rect(222, 262, 10,10, 0xffff);
    /* files */
    draw_menu_item();
      
}
/* user define action function*/
void touch_action() {
    uint16_t x, y;
    int temp_select;
    if (xpt2046_event & GPIO_IRQ_EDGE_FALL) {
            if (xpt2046_getXY(&x,&y)) { 
                if (x >= text_rgn_x0 && x <= text_rgn_x1 && y >= text_rgn_y0 && y <= text_rgn_y1) { 
                    temp_select = (int)((y-4)/item_height);
                    ili9341_fill_rect(2,sy+(temp_select)*item_height,215, 30, 0x001f);
                    ili9341_draw_string(sx, sy+(temp_select)*item_height,audio_files[temp_select+top_index], 0xffff, &font_fixedsys_mono_24);
                    ili9341_fill_rect(2,sy+(selected_item)*item_height,215, 30, 0x0000);
                    ili9341_draw_string(sx, sy+(selected_item)*item_height,audio_files[selected_item+top_index], 0xffff, &font_fixedsys_mono_24);
                    selected_item=temp_select;
                    user_action = ACTION_FILE;
                    strcpy(selected_filename, audio_files[selected_item+top_index]);
                }
                if (x >= play_rgn_x0 && x <= play_rgn_x1 && y >= play_rgn_y0 && y <= play_rgn_y1) {
                    user_action = ACTION_PLAY_PAUSE;
                    switch(i2s_play_state) {
                        case I2S_PLAYING:
                            i2s_play_state = I2S_PAUSE;
                            ili9341_draw_bitmap(10,277, &resume);
                        break;
                        case I2S_PAUSED:
                            i2s_play_state = I2S_RESUME;
                            ili9341_draw_bitmap(10,277, &pause);
                        break;
                        case I2S_STOP:
                            i2s_play_state = I2S_PLAY;
                        break;
                    }
                }
                if (x >= stop_rgn_x0 && x <= stop_rgn_x1 && y >= stop_rgn_y0 && y <= stop_rgn_y1) {
                    user_action = ACTION_STOP;
                    i2s_play_state=I2S_STOP;
                }
                if (x >= su_rgn_x0 && x <= su_rgn_x1 && y >= su_rgn_y0 && y <= su_rgn_y1) {
                    if (top_index > 0 && total_files > 9) {
                        top_index--;
                        draw_menu_item();
                    }
                }
                if (x >= sd_rgn_x0 && x <= sd_rgn_x1 && y >= sd_rgn_y0 && y <= sd_rgn_y1) { 
                    if (top_index+9 < total_files) {
                        top_index++;
                        draw_menu_item();
                    }
                }
            }
            xpt2046_event &= (!GPIO_IRQ_EDGE_FALL);
            gpio_set_irq_enabled(XPT2046_PEN_GPIO, GPIO_IRQ_EDGE_FALL, true);

        }
        if (xpt2046_event & GPIO_IRQ_EDGE_FALL) {
            xpt2046_event &= (!GPIO_IRQ_EDGE_RISE);
            gpio_set_irq_enabled(XPT2046_PEN_GPIO, GPIO_IRQ_EDGE_RISE, true);
        }

}

void touch_irq_handle(uint gpio, uint32_t event) {
    static uint32_t cnt=0;
    gpio_set_irq_enabled(gpio, GPIO_IRQ_EDGE_FALL&event, false);
    gpio_set_irq_enabled(gpio, GPIO_IRQ_EDGE_RISE&event, false);
    xpt2046_event |= event;
    if(event & GPIO_IRQ_EDGE_FALL && gpio == XPT2046_PEN_GPIO) { 
        touch_action();
    }
    if(event & GPIO_IRQ_EDGE_RISE  && gpio == XPT2046_PEN_GPIO) {

        
    }
 
}
bool xpt2046_getXY(uint16_t *x, uint16_t *y) {
   
    uint8_t temp[2];
    uint16_t raw_x, raw_y;
    uint32_t avg_x = 0;
    uint32_t avg_y = 0;
    uint8_t nsamples = 0;
    const uint8_t SAMPLES=16;

     gpio_put(XPT2046_CS, false);

    for(uint8_t i = 0; i < SAMPLES; i++, nsamples++)
    {
        if(gpio_get(XPT2046_PEN_GPIO))
            break;
        spi_write_blocking(XPT2046_SPI, &READ_X, 1);
        spi_read_blocking(XPT2046_SPI, 0x00, temp, 2);
        raw_x = ((uint16_t)temp[0]) << 8 | (uint16_t)temp[1];

        spi_write_blocking(XPT2046_SPI, &READ_Y, 1);
        spi_read_blocking(XPT2046_SPI, 0x00, temp, 2);
        raw_y = ((uint16_t)temp[0]) << 8 | (uint16_t)temp[1];

        avg_x += raw_x;
        avg_y += raw_y;
    }

    gpio_put(XPT2046_CS, true);

    if(nsamples < SAMPLES)
        return false;

    raw_x = (avg_x / SAMPLES);
    raw_y = (avg_y / SAMPLES);

    if(raw_x < XPT2046_MIN_RAW_X) raw_x = XPT2046_MIN_RAW_X;
    if(raw_x > XPT2046_MAX_RAW_X) raw_x = XPT2046_MAX_RAW_X;

    if(raw_y < XPT2046_MIN_RAW_Y) raw_y = XPT2046_MIN_RAW_Y;
    if(raw_y > XPT2046_MAX_RAW_Y) raw_y = XPT2046_MAX_RAW_Y;

    *x = (raw_x - XPT2046_MIN_RAW_X) * TFT_WIDTH  / (XPT2046_MAX_RAW_X - XPT2046_MIN_RAW_X);
    *y = (raw_y - XPT2046_MIN_RAW_Y) * TFT_HEIGHT / (XPT2046_MAX_RAW_Y - XPT2046_MIN_RAW_Y);
    return true;
    
}

void xpt2046_init() {
    gpio_init(XPT2046_PEN_GPIO);
    gpio_init(XPT2046_MISO);
    gpio_init(XPT2046_MOSI);
    gpio_init(XPT2046_CLK);
    gpio_init(XPT2046_CS);
    gpio_set_dir(XPT2046_CS, GPIO_OUT);
    gpio_set_function(XPT2046_CLK,GPIO_FUNC_SPI);
    gpio_set_function(XPT2046_CS,GPIO_FUNC_SIO);
    gpio_set_function(XPT2046_MOSI,GPIO_FUNC_SPI);
    gpio_set_function(XPT2046_MISO,GPIO_FUNC_SPI);
    spi_init(XPT2046_SPI,250000);

   gpio_set_irq_enabled_with_callback(XPT2046_PEN_GPIO, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, touch_irq_handle);
   

}





沒有留言:

張貼留言