本實驗整合
- [Raspberry Pi Pico (c-sdk)] Display: Ep 2 : PIO TFT LCD ILI9341 8-bit parallel C-code Driver
- [Raspberry Pi Pico (c-sdk)] Storage: Ep 3. Using SD/MMC Devices and Flash Devices Together
- 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。讀取觸控位置坐標,以便執行相關動作。
成果影片:
程式碼:
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);
}