mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
Add USB mass storage device (WIP) + rework task state machines
This commit is contained in:
parent
c2d3a0ae67
commit
085bc9e8f7
18 changed files with 913 additions and 246 deletions
|
|
@ -28,9 +28,7 @@ export default defineConfig({
|
|||
collapsed: false,
|
||||
items: [
|
||||
{ text: 'Bundles introduction', link: '/guide-intro' },
|
||||
{ text: 'Dev kit (Raspberry Pico)', link: '/guide-devkit-pico' },
|
||||
{ text: 'SeeedStudio Wio Lite', link: '/guide-wio-lite' },
|
||||
{ text: 'Arduino MKR Zero', link: '/guide-mkr-zero' }
|
||||
{ text: 'Dev kit (Raspberry Pico)', link: '/guide-devkit-pico' }
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -24,22 +24,22 @@ Here is an example of components.
|
|||
| 2inch LCD (320x240) | 14 € | Waveshare | [Buy on Amazon](https://amzn.to/3LyG5oJ) |
|
||||
| Some push buttons and rotary switches | 4 € | Any | [Buy on Amazon](https://amzn.to/3AX6MOX) |
|
||||
| UPS module or Pimoroni LiPo Shim | 15 € | Waveshare | [Buy on Amazon](https://amzn.to/44p8Exo) |
|
||||
| LiPo battery 500mAh | 9 € | Any | [Buy on Amazon](https://amzn.to/3VCl3df) |
|
||||
| LiPo battery 500mAh | 9 € | Any | [Buy on Amazon](https://amzn.to/3VCl3df) |
|
||||
| GPIO Expander Pico 4 modules | 17 € | Waveshare | [Buy on Amazon](https://amzn.to/42ukJQ4) |
|
||||
| SDCard breakout board | 5 € | Any | |
|
||||
| SDCard breakout board | 5 € | Any | [Buy on Amazon](https://amzn.to/3qf3chr) | |
|
||||
| **TOTAL** | **86 €** |
|
||||
|
||||
|
||||
A much more optimization of price is possible using the Marble Pico board which is completely compatible to the original Pico pinout but with embedded battery management and SDCard holder. Price is still low: 5€!
|
||||
|
||||
| Part | Price | Shop | Link |
|
||||
| ------------------------------------- | -------- | --------- | ---------------------------------------- |
|
||||
| Audio board + speaker | 13 € | Waveshare | [Buy on Amazon](https://amzn.to/41nWgeB) |
|
||||
| Marble Pico | 5 € | ArduShop | [Buy on Amazon](https://ardushop.ro/en/home/2652-marble-pico.html) |
|
||||
| 2inch LCD (320x240) | 14 € | Waveshare | [Buy on Amazon](https://amzn.to/3LyG5oJ) |
|
||||
| Some push buttons and rotary switches | 4 € | Any | [Buy on Amazon](https://amzn.to/3AX6MOX) |
|
||||
| LiPo battery 500mAh | 9 € | Any | [Buy on Amazon](https://amzn.to/3VCl3df) |
|
||||
| GPIO Expander Pico 2 modules | 14 € | Waveshare | [Buy on Amazon](https://amzn.to/42ukJQ4) |
|
||||
| Part | Price | Shop | Link |
|
||||
| ------------------------------------- | -------- | --------- | ----------------------------------------------------------------------- |
|
||||
| Audio board + speaker | 13 € | Waveshare | [Buy on Amazon](https://amzn.to/41nWgeB) |
|
||||
| Marble Pico | 5 € | ArduShop | [Buy on ardushop.ro](https://ardushop.ro/en/home/2652-marble-pico.html) |
|
||||
| 2inch LCD (320x240) | 14 € | Waveshare | [Buy on Amazon](https://amzn.to/3LyG5oJ) |
|
||||
| Some push buttons and rotary switches | 4 € | Any | [Buy on Amazon](https://amzn.to/3AX6MOX) |
|
||||
| LiPo battery 500mAh | 9 € | Any | [Buy on Amazon](https://amzn.to/3VCl3df) |
|
||||
| GPIO Expander Pico 2 modules | 14 € | Waveshare | [Buy on Amazon](https://amzn.to/42ukJQ4) |
|
||||
| **TOTAL** | **59 €** |
|
||||
|
||||
|
||||
|
|
|
|||
5
software/.vscode/settings.json
vendored
5
software/.vscode/settings.json
vendored
|
|
@ -38,6 +38,9 @@
|
|||
"critical_section.h": "c",
|
||||
"serializers.h": "c",
|
||||
"cstring": "c",
|
||||
"typeinfo": "c"
|
||||
"typeinfo": "c",
|
||||
"mutex.h": "c",
|
||||
"sem.h": "c",
|
||||
"msc_disk.h": "c"
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,9 @@ set(PICO_SRCS
|
|||
${CMAKE_CURRENT_LIST_DIR}/pico_hal_wrapper.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/pico_lcd_spi.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/pico_i2s.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/msc_disk.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/usb_descriptors.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/pico_sdcard_spi.c
|
||||
)
|
||||
|
||||
include_directories(../../src ../../hal ../../library .)
|
||||
|
|
@ -30,7 +33,7 @@ add_library(
|
|||
pico_generate_pio_header(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}/pico_i2s.pio)
|
||||
pico_generate_pio_header(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}/i2s.pio)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE pico_stdlib hardware_exception cmsis_core)
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE pico_stdlib hardware_exception cmsis_core tinyusb_device tinyusb_board)
|
||||
target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
target_sources(${PROJECT_NAME} INTERFACE
|
||||
|
|
|
|||
178
software/platform/raspberry-pico-w/msc_disk.c
Normal file
178
software/platform/raspberry-pico-w/msc_disk.c
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
#include "bsp/board.h"
|
||||
#include "tusb.h"
|
||||
#include "class/msc/msc.h"
|
||||
|
||||
#include "pico.h"
|
||||
#include "filesystem.h"
|
||||
|
||||
// whether host does safe-eject
|
||||
static bool ejected = false;
|
||||
|
||||
// Invoked when received SCSI_CMD_INQUIRY
|
||||
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
|
||||
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
|
||||
{
|
||||
(void)lun;
|
||||
|
||||
const char vid[] = "Microdia USB2.0 DSP";
|
||||
const char pid[] = "Mass Storage";
|
||||
const char rev[] = "1.0";
|
||||
|
||||
memcpy(vendor_id, vid, strlen(vid));
|
||||
memcpy(product_id, pid, strlen(pid));
|
||||
memcpy(product_rev, rev, strlen(rev));
|
||||
}
|
||||
|
||||
// Invoked when received Test Unit Ready command.
|
||||
// return true allowing host to read/write this LUN e.g SD card inserted
|
||||
bool tud_msc_test_unit_ready_cb(uint8_t lun)
|
||||
{
|
||||
(void)lun;
|
||||
|
||||
// RAM disk is ready until ejected
|
||||
if (ejected)
|
||||
{
|
||||
// Additional Sense 3A-00 is NOT_FOUND
|
||||
tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
|
||||
// Application update block count and block size
|
||||
void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size)
|
||||
{
|
||||
(void)lun;
|
||||
|
||||
*block_count = filesystem_get_capacity();
|
||||
*block_size = 512;
|
||||
}
|
||||
|
||||
// Invoked when received Start Stop Unit command
|
||||
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
|
||||
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
|
||||
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
|
||||
{
|
||||
(void)lun;
|
||||
(void)power_condition;
|
||||
|
||||
if (load_eject)
|
||||
{
|
||||
if (start)
|
||||
{
|
||||
// load disk storage
|
||||
}
|
||||
else
|
||||
{
|
||||
// unload disk storage
|
||||
ejected = true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8_t blockBuf[512];
|
||||
|
||||
#include "fs_task.h"
|
||||
#include "qor.h"
|
||||
static qor_mbox_t ReadFsMailBox;
|
||||
static uint8_t *ReadFsEventQueue[10];
|
||||
|
||||
void msc_disk_initialize()
|
||||
{
|
||||
qor_mbox_init(&ReadFsMailBox, (void **)&ReadFsEventQueue, 10);
|
||||
}
|
||||
|
||||
void fs_read_cb(bool success)
|
||||
{
|
||||
(void)success;
|
||||
static uint8_t dummy;
|
||||
qor_mbox_notify(&ReadFsMailBox, (void **)&dummy, QOR_MBOX_OPTION_SEND_BACK);
|
||||
}
|
||||
|
||||
// Callback invoked when received READ10 command.
|
||||
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
|
||||
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize)
|
||||
{
|
||||
(void)lun;
|
||||
|
||||
// out of ramdisk
|
||||
// if (lba >= DISK_BLOCK_NUM)
|
||||
// return -1;
|
||||
|
||||
fs_task_read_block(lba, blockBuf, fs_read_cb);
|
||||
|
||||
uint8_t *ev;
|
||||
uint32_t res = qor_mbox_wait(&ReadFsMailBox, (void **)&ev, 200);
|
||||
|
||||
// printf("lba 0x%x, bufsize %d, offset %d\n",lba, bufsize, offset);
|
||||
// uint8_t const *addr = msc_disk[lba] + offset;
|
||||
|
||||
uint8_t const *addr = blockBuf + offset;
|
||||
memcpy(buffer, addr, bufsize);
|
||||
|
||||
return (int32_t)bufsize;
|
||||
}
|
||||
|
||||
bool tud_msc_is_writable_cb(uint8_t lun)
|
||||
{
|
||||
(void)lun;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback invoked when received WRITE10 command.
|
||||
// Process data in buffer to disk's storage and return number of written bytes
|
||||
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize)
|
||||
{
|
||||
(void)lun;
|
||||
printf("write - lba 0x%x, bufsize%d\n", lba, bufsize);
|
||||
|
||||
return (int32_t)bufsize;
|
||||
}
|
||||
|
||||
// Callback invoked when received an SCSI command not in built-in list below
|
||||
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
|
||||
// - READ10 and WRITE10 has their own callbacks
|
||||
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize)
|
||||
{
|
||||
// read10 & write10 has their own callback and MUST not be handled here
|
||||
|
||||
void const *response = NULL;
|
||||
int32_t resplen = 0;
|
||||
|
||||
// most scsi handled is input
|
||||
bool in_xfer = true;
|
||||
|
||||
switch (scsi_cmd[0])
|
||||
{
|
||||
default:
|
||||
// Set Sense = Invalid Command Operation
|
||||
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
|
||||
|
||||
// negative means error -> tinyusb could stall and/or response with failed status
|
||||
resplen = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
// return resplen must not larger than bufsize
|
||||
if (resplen > bufsize)
|
||||
resplen = bufsize;
|
||||
|
||||
if (response && (resplen > 0))
|
||||
{
|
||||
if (in_xfer)
|
||||
{
|
||||
memcpy(buffer, response, (size_t)resplen);
|
||||
}
|
||||
else
|
||||
{
|
||||
// SCSI output
|
||||
}
|
||||
}
|
||||
|
||||
return (int32_t)resplen;
|
||||
}
|
||||
5
software/platform/raspberry-pico-w/msc_disk.h
Normal file
5
software/platform/raspberry-pico-w/msc_disk.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
void printReserveSectFat();
|
||||
|
||||
void msc_disk_initialize();
|
||||
71
software/platform/raspberry-pico-w/pico_sdcard_spi.c
Normal file
71
software/platform/raspberry-pico-w/pico_sdcard_spi.c
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "hardware/spi.h"
|
||||
#include "hardware/dma.h"
|
||||
|
||||
// Grab some unused dma channels
|
||||
static uint dma_tx;
|
||||
static uint dma_rx;
|
||||
|
||||
void pico_sdcard_dma_initialize()
|
||||
{
|
||||
dma_tx = dma_claim_unused_channel(true);
|
||||
dma_rx = dma_claim_unused_channel(true);
|
||||
|
||||
// We set the outbound DMA to transfer from a memory buffer to the SPI transmit FIFO paced by the SPI TX FIFO DREQ
|
||||
// The default is for the read address to increment every element (in this case 1 byte = DMA_SIZE_8)
|
||||
// and for the write address to remain unchanged.
|
||||
|
||||
// printf("Configure TX DMA\n");
|
||||
dma_channel_config c = dma_channel_get_default_config(dma_tx);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
|
||||
channel_config_set_dreq(&c, spi_get_dreq(spi0, true));
|
||||
dma_channel_configure(dma_tx, &c,
|
||||
&spi_get_hw(spi0)->dr, // write address
|
||||
NULL, // read address
|
||||
0, // element count will be set later
|
||||
false); // don't start yet
|
||||
|
||||
// printf("Configure RX DMA\n");
|
||||
|
||||
// We set the inbound DMA to transfer from the SPI receive FIFO to a memory buffer paced by the SPI RX FIFO DREQ
|
||||
// We configure the read address to remain unchanged for each element, but the write
|
||||
// address to increment (so data is written throughout the buffer)
|
||||
c = dma_channel_get_default_config(dma_rx);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
|
||||
channel_config_set_dreq(&c, spi_get_dreq(spi0, false));
|
||||
channel_config_set_read_increment(&c, false);
|
||||
channel_config_set_write_increment(&c, true);
|
||||
dma_channel_configure(dma_rx, &c,
|
||||
NULL, // write address
|
||||
&spi_get_hw(spi0)->dr, // read address
|
||||
0, // element count (each element is of size transfer_data_size)
|
||||
false); // don't start yet
|
||||
|
||||
// printf("Starting DMAs...\n");
|
||||
// start them exactly simultaneously to avoid races (in extreme cases the FIFO could overflow)
|
||||
dma_start_channel_mask((1u << dma_tx) | (1u << dma_rx));
|
||||
// printf("Wait for RX complete...\n");
|
||||
|
||||
/*
|
||||
dma_channel_wait_for_finish_blocking(dma_rx);
|
||||
if (dma_channel_is_busy(dma_tx))
|
||||
{
|
||||
panic("RX completed before TX");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void pico_sdcard_dma_start_write(uint8_t *buffer, uint32_t size)
|
||||
{
|
||||
// irq_set_exclusive_handler(DMA_IRQ_1, dma_handler);
|
||||
|
||||
// // D'abord on va paramétrer les diverses interruptions
|
||||
// dma_channel_set_irq0_enabled(i2s->dma_ch_out_data, true);
|
||||
// irq_set_enabled(DMA_IRQ_1, true);
|
||||
// dma_channel_start(i2s->dma_ch_out_ctrl);
|
||||
}
|
||||
1
software/platform/raspberry-pico-w/pico_sdcard_spi.h
Normal file
1
software/platform/raspberry-pico-w/pico_sdcard_spi.h
Normal file
|
|
@ -0,0 +1 @@
|
|||
#pragma once
|
||||
87
software/platform/raspberry-pico-w/tusb_config.h
Normal file
87
software/platform/raspberry-pico-w/tusb_config.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
#ifndef _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// defined by board.mk
|
||||
#ifndef CFG_TUSB_MCU
|
||||
#error CFG_TUSB_MCU must be defined
|
||||
#endif
|
||||
|
||||
// RHPort number used for device can be defined by board.mk, default to port 0
|
||||
#ifndef BOARD_DEVICE_RHPORT_NUM
|
||||
#define BOARD_DEVICE_RHPORT_NUM 0
|
||||
#endif
|
||||
|
||||
// RHPort max operational speed can defined by board.mk
|
||||
// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed
|
||||
#ifndef BOARD_DEVICE_RHPORT_SPEED
|
||||
#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \
|
||||
CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X)
|
||||
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED
|
||||
#else
|
||||
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Device mode with rhport and speed defined by board.mk
|
||||
#if BOARD_DEVICE_RHPORT_NUM == 0
|
||||
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
|
||||
#elif BOARD_DEVICE_RHPORT_NUM == 1
|
||||
#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
|
||||
#else
|
||||
#error "Incorrect RHPort configuration"
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_OS
|
||||
#define CFG_TUSB_OS OPT_OS_NONE
|
||||
#endif
|
||||
|
||||
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
|
||||
// #define CFG_TUSB_DEBUG 0
|
||||
|
||||
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
|
||||
* Tinyusb use follows macros to declare transferring memory so that they can be put
|
||||
* into those specific section.
|
||||
* e.g
|
||||
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
|
||||
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
|
||||
*/
|
||||
#ifndef CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_HID 0
|
||||
#define CFG_TUD_CDC 0
|
||||
#define CFG_TUD_MSC 1
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_VENDOR 0
|
||||
|
||||
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||
#define CFG_TUD_HID_EP_BUFSIZE 16
|
||||
#define CFG_TUD_MSC_EP_BUFSIZE 512
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CONFIG_H_ */
|
||||
220
software/platform/raspberry-pico-w/usb_descriptors.c
Normal file
220
software/platform/raspberry-pico-w/usb_descriptors.c
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
#include "tusb.h"
|
||||
#include "class/msc/msc.h"
|
||||
#include "device/usbd.h"
|
||||
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
|
||||
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
|
||||
*
|
||||
* Auto ProductID layout's Bitmap:
|
||||
* [MSB] HID | MSC | CDC [LSB]
|
||||
*/
|
||||
#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n))
|
||||
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
|
||||
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4))
|
||||
|
||||
#define USB_VID 0x0c45
|
||||
#define USB_BCD 0x6840
|
||||
|
||||
// Bus 003 Device 075: ID 0c45:6840 Microdia USB2.0 DSP
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Device Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
tusb_desc_device_t const desc_device =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = USB_BCD,
|
||||
|
||||
// Use Interface Association Descriptor (IAD) for CDC
|
||||
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = USB_VID,
|
||||
.idProduct = USB_PID,
|
||||
.bcdDevice = 0x0100,
|
||||
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
|
||||
.bNumConfigurations = 0x01};
|
||||
|
||||
// Invoked when received GET DEVICE DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
uint8_t const *tud_descriptor_device_cb(void)
|
||||
{
|
||||
return (uint8_t const *)&desc_device;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Configuration Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
enum
|
||||
{
|
||||
ITF_NUM_MSC,
|
||||
ITF_NUM_TOTAL
|
||||
};
|
||||
|
||||
#define EPNUM_MSC_OUT 0x01
|
||||
#define EPNUM_MSC_IN 0x81
|
||||
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)
|
||||
|
||||
// full speed configuration
|
||||
uint8_t const desc_fs_configuration[] =
|
||||
{
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
|
||||
|
||||
// Interface number, string index, EP Out & EP In address, EP size
|
||||
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 3, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64),
|
||||
};
|
||||
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
|
||||
|
||||
// high speed configuration
|
||||
uint8_t const desc_hs_configuration[] =
|
||||
{
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
|
||||
// Interface number, string index, EP Out & EP In address, EP size
|
||||
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 3, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512),
|
||||
};
|
||||
|
||||
// other speed configuration
|
||||
uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
|
||||
|
||||
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
|
||||
tusb_desc_device_qualifier_t const desc_device_qualifier =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_qualifier_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
|
||||
.bcdUSB = USB_BCD,
|
||||
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.bNumConfigurations = 0x01,
|
||||
.bReserved = 0x00};
|
||||
|
||||
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
|
||||
// device_qualifier descriptor describes information about a high-speed capable device that would
|
||||
// change if the device were operating at the other speed. If not highspeed capable stall this request.
|
||||
uint8_t const *tud_descriptor_device_qualifier_cb(void)
|
||||
{
|
||||
return (uint8_t const *)&desc_device_qualifier;
|
||||
}
|
||||
|
||||
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
|
||||
uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index)
|
||||
{
|
||||
(void)index; // for multiple configurations
|
||||
|
||||
// if link speed is high return fullspeed config, and vice versa
|
||||
// Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG
|
||||
memcpy(desc_other_speed_config,
|
||||
(tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration,
|
||||
CONFIG_TOTAL_LEN);
|
||||
|
||||
desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
|
||||
|
||||
return desc_other_speed_config;
|
||||
}
|
||||
|
||||
#endif // highspeed
|
||||
|
||||
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
(void)index; // for multiple configurations
|
||||
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
// Although we are highspeed, host may be fullspeed.
|
||||
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
|
||||
#else
|
||||
return desc_fs_configuration;
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// String Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/*
|
||||
T: Bus=03 Lev=03 Prnt=04 Port=00 Cnt=01 Dev#= 75 Spd=480 MxCh= 0
|
||||
D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
|
||||
P: Vendor=0c45 ProdID=6840 Rev=01.00
|
||||
S: Product=USB2.0 DSP
|
||||
S: SerialNumber=USB2.0 DSP
|
||||
C: #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr=500mA
|
||||
I: If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage
|
||||
E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
|
||||
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
|
||||
|
||||
|
||||
*/
|
||||
|
||||
// array of pointer to string descriptors
|
||||
char const *string_desc_arr[] =
|
||||
{
|
||||
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
|
||||
"D8S EURL", // 1: Manufacturer
|
||||
"USB2.0 DSP", // 2: Product
|
||||
"USB2.0 DSP", // 3: MSC Interface
|
||||
};
|
||||
|
||||
static uint16_t _desc_str[32];
|
||||
|
||||
// Invoked when received GET STRING DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
(void)langid;
|
||||
|
||||
uint8_t chr_count;
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||
chr_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
|
||||
|
||||
if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])))
|
||||
return NULL;
|
||||
|
||||
const char *str = string_desc_arr[index];
|
||||
|
||||
// Cap at max char
|
||||
chr_count = (uint8_t)strlen(str);
|
||||
if (chr_count > 31)
|
||||
chr_count = 31;
|
||||
|
||||
// Convert ASCII string into UTF-16
|
||||
for (uint8_t i = 0; i < chr_count; i++)
|
||||
{
|
||||
_desc_str[1 + i] = str[i];
|
||||
}
|
||||
}
|
||||
|
||||
// first byte is length (including header), second byte is string type
|
||||
_desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
|
||||
|
||||
return _desc_str;
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
#include "filesystem.h"
|
||||
#include "mini_qoi.h"
|
||||
#include "serializers.h"
|
||||
#include "sdcard.h"
|
||||
|
||||
#ifdef OST_USE_FF_LIBRARY
|
||||
#include "ff.h"
|
||||
|
|
@ -28,6 +29,8 @@ static FIL File[2]; /* File object */
|
|||
static DIR Dir; /* Directory object */
|
||||
static FILINFO Finfo;
|
||||
|
||||
static SD_CardInfo cardinfo;
|
||||
|
||||
file_t file_open(const char *filename)
|
||||
{
|
||||
#ifdef OST_USE_FF_LIBRARY
|
||||
|
|
@ -95,9 +98,24 @@ void filesystem_mount()
|
|||
debug_printf("[OST] SD Card File System = %d\r\n", fs.fs_type); // FS_EXFAT = 4
|
||||
// debug_printf("[OST] Starting with CPU=%d\r\n", (int)SystemCoreClock);
|
||||
|
||||
// Get SD Card info
|
||||
if (sdcard_get_card_info(&cardinfo) == SD_RESPONSE_NO_ERROR)
|
||||
{
|
||||
debug_printf("[OST] Sectors: %d, sector size: %d\r\n", cardinfo.CardCapacity, cardinfo.CardBlockSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_printf("[OST] Cannot read SDCard info\r\n");
|
||||
}
|
||||
|
||||
scan_files("");
|
||||
}
|
||||
|
||||
uint32_t filesystem_get_capacity()
|
||||
{
|
||||
return cardinfo.CardCapacity;
|
||||
}
|
||||
|
||||
/*
|
||||
// Loop in all directories
|
||||
void disk_start()
|
||||
|
|
|
|||
|
|
@ -9,5 +9,6 @@ void filesystem_mount();
|
|||
void filesystem_display_image(const char *filename);
|
||||
void filesystem_load_rom(uint8_t *mem, const char *filename);
|
||||
void filesystem_get_story_title(ost_context_t *ctx);
|
||||
uint32_t filesystem_get_capacity();
|
||||
|
||||
#endif // FILESYSTEM_H
|
||||
|
|
|
|||
|
|
@ -23,55 +23,56 @@
|
|||
#include "system.h"
|
||||
#include "vm_task.h"
|
||||
#include "fs_task.h"
|
||||
#include "sdcard.h"
|
||||
|
||||
// ===========================================================================================================
|
||||
// DEFINITIONS
|
||||
// ===========================================================================================================
|
||||
typedef struct
|
||||
{
|
||||
uint8_t ev;
|
||||
} ost_audio_event_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FS_WAIT_FOR_EVENT,
|
||||
FS_NO_EVENT,
|
||||
FS_PLAY_SOUND,
|
||||
FS_DISPLAY_IMAGE,
|
||||
FS_LOAD_INDEX,
|
||||
FS_LOAD_STORY
|
||||
FS_LOAD_STORY,
|
||||
FS_READ_SDCARD_BLOCK,
|
||||
FS_AUDIO_NEXT_SAMPLES
|
||||
} fs_state_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
fs_state_t ev;
|
||||
uint8_t *mem;
|
||||
uint32_t addr;
|
||||
ost_button_t button;
|
||||
char *image;
|
||||
char *sound;
|
||||
fs_result_cb_t cb;
|
||||
} ost_fs_event_t;
|
||||
|
||||
#define STORY_DIR_OFFSET (UUID_SIZE + 1)
|
||||
|
||||
// ===========================================================================================================
|
||||
// PRIVATE GLOBAL VARIABLES
|
||||
// ===========================================================================================================
|
||||
static qor_tcb_t FsTcb;
|
||||
static uint32_t FsStack[4096];
|
||||
|
||||
static qor_mbox_t AudioMailBox;
|
||||
|
||||
static ost_audio_event_t wake_up;
|
||||
|
||||
static ost_audio_event_t *AudioQueue[10];
|
||||
|
||||
static int dbg_state = 0;
|
||||
|
||||
static fs_state_t FsState = FS_WAIT_FOR_EVENT;
|
||||
|
||||
static qor_mbox_t FsMailBox;
|
||||
|
||||
static ost_fs_event_t *FsEventQueue[10];
|
||||
|
||||
static ost_context_t OstContext;
|
||||
|
||||
static int PacketCounter = 0;
|
||||
|
||||
static char ScratchFile[260];
|
||||
|
||||
static const char *ImagesDir = "/images/";
|
||||
static const char *SoundsDir = "/sounds/";
|
||||
|
||||
static uint8_t LedState = 0;
|
||||
|
||||
// ===========================================================================================================
|
||||
// FILE SYSTEM TASK
|
||||
// ===========================================================================================================
|
||||
|
|
@ -79,8 +80,9 @@ static ost_context_t OstContext;
|
|||
// End of DMA transfer callback
|
||||
static void audio_callback(void)
|
||||
{
|
||||
dbg_state = 1 - dbg_state;
|
||||
qor_mbox_notify(&AudioMailBox, (void **)&wake_up, QOR_MBOX_OPTION_SEND_BACK);
|
||||
static ost_fs_event_t NextSamplesEv = {
|
||||
.ev = FS_AUDIO_NEXT_SAMPLES};
|
||||
qor_mbox_notify(&FsMailBox, (void **)&NextSamplesEv, QOR_MBOX_OPTION_SEND_BACK);
|
||||
}
|
||||
|
||||
static void show_duration(uint32_t millisecondes)
|
||||
|
|
@ -97,165 +99,173 @@ static void show_duration(uint32_t millisecondes)
|
|||
debug_printf("Temps : %d minutes, %d secondes, %d millisecondes\r\n", minutes, secondes, reste);
|
||||
}
|
||||
|
||||
static void play_sound_file(const char *filename)
|
||||
{
|
||||
debug_printf("\r\n-------------------------------------------------------\r\nPlaying: %s\r\n", filename);
|
||||
ost_system_stopwatch_start();
|
||||
ost_audio_play(filename);
|
||||
|
||||
ost_audio_event_t *e = NULL;
|
||||
|
||||
int isPlaying = 0;
|
||||
int count = 0;
|
||||
uint32_t res = 0;
|
||||
do
|
||||
{
|
||||
uint32_t res = qor_mbox_wait(&AudioMailBox, (void **)&e, 300); // On devrait recevoir un message toutes les 3ms (durée d'envoi d'un buffer I2S)
|
||||
|
||||
if (res == QOR_MBOX_OK)
|
||||
{
|
||||
isPlaying = ost_audio_process();
|
||||
}
|
||||
|
||||
count++;
|
||||
|
||||
} while (isPlaying);
|
||||
|
||||
uint32_t executionTime = ost_system_stopwatch_stop();
|
||||
ost_audio_stop();
|
||||
|
||||
debug_printf("\r\nPackets: %d\r\n", count);
|
||||
show_duration(executionTime);
|
||||
}
|
||||
|
||||
static char ScratchFile[260];
|
||||
|
||||
static const char *ImagesDir = "/images/";
|
||||
static const char *SoundsDir = "/sounds/";
|
||||
|
||||
#define STORY_DIR_OFFSET (UUID_SIZE + 1)
|
||||
|
||||
static uint8_t LedState = 0;
|
||||
|
||||
void FsTask(void *args)
|
||||
{
|
||||
ost_fs_event_t *fs_ev = NULL;
|
||||
ost_fs_event_t *message = NULL;
|
||||
uint32_t res = 0;
|
||||
|
||||
filesystem_read_index_file(&OstContext);
|
||||
FsState = FS_LOAD_INDEX;
|
||||
|
||||
int isPlaying = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
switch (FsState)
|
||||
res = qor_mbox_wait(&FsMailBox, (void **)&message, 1000);
|
||||
if (res == QOR_MBOX_OK)
|
||||
{
|
||||
case FS_PLAY_SOUND:
|
||||
if (OstContext.sound != NULL)
|
||||
switch (message->ev)
|
||||
{
|
||||
case FS_PLAY_SOUND:
|
||||
if (OstContext.sound != NULL)
|
||||
{
|
||||
ScratchFile[STORY_DIR_OFFSET] = 0;
|
||||
strcat(ScratchFile, SoundsDir);
|
||||
strcat(ScratchFile, message->sound);
|
||||
|
||||
debug_printf("\r\n-------------------------------------------------------\r\nPlaying: %s\r\n", ScratchFile);
|
||||
|
||||
ost_system_stopwatch_start();
|
||||
ost_audio_play(ScratchFile);
|
||||
PacketCounter = 0;
|
||||
isPlaying = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case FS_AUDIO_NEXT_SAMPLES:
|
||||
isPlaying = ost_audio_process();
|
||||
PacketCounter++;
|
||||
|
||||
if (isPlaying == 0)
|
||||
{
|
||||
uint32_t executionTime = ost_system_stopwatch_stop();
|
||||
ost_audio_stop();
|
||||
|
||||
debug_printf("\r\nPackets: %d\r\n", PacketCounter);
|
||||
show_duration(executionTime);
|
||||
vm_task_sound_finished();
|
||||
}
|
||||
break;
|
||||
|
||||
case FS_DISPLAY_IMAGE:
|
||||
|
||||
if (OstContext.image != NULL)
|
||||
{
|
||||
ScratchFile[STORY_DIR_OFFSET] = 0;
|
||||
strcat(ScratchFile, ImagesDir);
|
||||
strcat(ScratchFile, message->image);
|
||||
|
||||
filesystem_display_image(ScratchFile);
|
||||
}
|
||||
break;
|
||||
|
||||
case FS_LOAD_INDEX:
|
||||
{
|
||||
bool success = false;
|
||||
if (OstContext.number_of_stories > 0)
|
||||
{
|
||||
filesystem_get_story_title(&OstContext);
|
||||
|
||||
// Init current directory
|
||||
ScratchFile[0] = '/';
|
||||
memcpy(&ScratchFile[1], OstContext.uuid, UUID_SIZE);
|
||||
ScratchFile[1 + UUID_SIZE] = 0;
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (message->cb != NULL)
|
||||
{
|
||||
message->cb(success);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FS_LOAD_STORY:
|
||||
ScratchFile[STORY_DIR_OFFSET] = 0;
|
||||
strcat(ScratchFile, SoundsDir);
|
||||
strcat(ScratchFile, OstContext.sound);
|
||||
play_sound_file(ScratchFile);
|
||||
}
|
||||
FsState = FS_WAIT_FOR_EVENT;
|
||||
break;
|
||||
case FS_DISPLAY_IMAGE:
|
||||
strcat(ScratchFile, "/story.c32");
|
||||
filesystem_load_rom(message->mem, ScratchFile);
|
||||
// ROM loaded, execute story
|
||||
vm_task_start_story();
|
||||
break;
|
||||
|
||||
if (OstContext.image != NULL)
|
||||
{
|
||||
ScratchFile[STORY_DIR_OFFSET] = 0;
|
||||
strcat(ScratchFile, ImagesDir);
|
||||
strcat(ScratchFile, OstContext.image);
|
||||
case FS_READ_SDCARD_BLOCK:
|
||||
sdcard_sector_read(message->addr, message->mem);
|
||||
if (message->cb != NULL)
|
||||
{
|
||||
message->cb(true);
|
||||
}
|
||||
break;
|
||||
|
||||
filesystem_display_image(ScratchFile);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (OstContext.sound != NULL)
|
||||
{
|
||||
FsState = FS_PLAY_SOUND;
|
||||
}
|
||||
else
|
||||
{
|
||||
FsState = FS_WAIT_FOR_EVENT;
|
||||
}
|
||||
break;
|
||||
|
||||
case FS_LOAD_INDEX:
|
||||
if (OstContext.number_of_stories > 0)
|
||||
{
|
||||
filesystem_get_story_title(&OstContext);
|
||||
|
||||
// Init current directory
|
||||
ScratchFile[0] = '/';
|
||||
memcpy(&ScratchFile[1], OstContext.uuid, UUID_SIZE);
|
||||
ScratchFile[1 + UUID_SIZE] = 0;
|
||||
|
||||
FsState = FS_DISPLAY_IMAGE; // Always display image (then sound), if there is one
|
||||
}
|
||||
else
|
||||
{
|
||||
FsState = FS_WAIT_FOR_EVENT;
|
||||
}
|
||||
break;
|
||||
case FS_LOAD_STORY:
|
||||
ScratchFile[STORY_DIR_OFFSET] = 0;
|
||||
strcat(ScratchFile, "/story.c32");
|
||||
filesystem_load_rom(fs_ev->mem, ScratchFile);
|
||||
// ROM loaded, execute story
|
||||
vm_task_start_story();
|
||||
FsState = FS_WAIT_FOR_EVENT;
|
||||
break;
|
||||
|
||||
case FS_WAIT_FOR_EVENT:
|
||||
default:
|
||||
res = qor_mbox_wait(&FsMailBox, (void **)&fs_ev, 1000);
|
||||
if (res == QOR_MBOX_OK)
|
||||
{
|
||||
// valid event, accept it
|
||||
FsState = fs_ev->ev;
|
||||
OstContext.image = fs_ev->image;
|
||||
OstContext.sound = fs_ev->sound;
|
||||
}
|
||||
else
|
||||
{
|
||||
LedState = 1 - LedState;
|
||||
ost_hal_gpio_set(OST_GPIO_DEBUG_LED, LedState);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LedState = 1 - LedState;
|
||||
ost_hal_gpio_set(OST_GPIO_DEBUG_LED, LedState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fs_task_scan_index()
|
||||
void fs_task_read_block(uint32_t addr, uint8_t *block, fs_result_cb_t cb)
|
||||
{
|
||||
static ost_fs_event_t ReadBlockEv = {
|
||||
.ev = FS_READ_SDCARD_BLOCK};
|
||||
|
||||
ReadBlockEv.mem = block;
|
||||
ReadBlockEv.addr = addr;
|
||||
ReadBlockEv.cb = cb;
|
||||
qor_mbox_notify(&FsMailBox, (void **)&ReadBlockEv, QOR_MBOX_OPTION_SEND_BACK);
|
||||
}
|
||||
|
||||
void fs_task_scan_index(fs_result_cb_t cb)
|
||||
{
|
||||
static ost_fs_event_t ScanIndexEv = {
|
||||
.ev = FS_LOAD_INDEX};
|
||||
|
||||
.ev = FS_LOAD_INDEX,
|
||||
.cb = NULL};
|
||||
ScanIndexEv.cb = cb;
|
||||
qor_mbox_notify(&FsMailBox, (void **)&ScanIndexEv, QOR_MBOX_OPTION_SEND_BACK);
|
||||
}
|
||||
|
||||
void fs_task_play_index()
|
||||
{
|
||||
fs_task_image_start(OstContext.image);
|
||||
fs_task_sound_start(OstContext.sound);
|
||||
}
|
||||
|
||||
void fs_task_load_story(uint8_t *mem)
|
||||
{
|
||||
static ost_fs_event_t LoadRomxEv = {
|
||||
.ev = FS_LOAD_STORY};
|
||||
.ev = FS_LOAD_STORY,
|
||||
.cb = NULL};
|
||||
|
||||
LoadRomxEv.mem = mem;
|
||||
qor_mbox_notify(&FsMailBox, (void **)&LoadRomxEv, QOR_MBOX_OPTION_SEND_BACK);
|
||||
}
|
||||
|
||||
void fs_task_media_start(char *image, char *sound)
|
||||
void fs_task_sound_start(char *sound)
|
||||
{
|
||||
static ost_fs_event_t MediaStartEv = {
|
||||
.ev = FS_DISPLAY_IMAGE};
|
||||
.ev = FS_PLAY_SOUND,
|
||||
.cb = NULL};
|
||||
|
||||
MediaStartEv.image = NULL;
|
||||
MediaStartEv.sound = sound;
|
||||
qor_mbox_notify(&FsMailBox, (void **)&MediaStartEv, QOR_MBOX_OPTION_SEND_BACK);
|
||||
}
|
||||
|
||||
void fs_task_image_start(char *image)
|
||||
{
|
||||
static ost_fs_event_t MediaStartEv = {
|
||||
.ev = FS_DISPLAY_IMAGE,
|
||||
.cb = NULL};
|
||||
|
||||
MediaStartEv.image = image;
|
||||
MediaStartEv.sound = sound;
|
||||
MediaStartEv.sound = NULL;
|
||||
qor_mbox_notify(&FsMailBox, (void **)&MediaStartEv, QOR_MBOX_OPTION_SEND_BACK);
|
||||
}
|
||||
|
||||
void fs_task_initialize()
|
||||
{
|
||||
qor_mbox_init(&AudioMailBox, (void **)&AudioQueue, 10);
|
||||
qor_mbox_init(&FsMailBox, (void **)&FsEventQueue, 10);
|
||||
|
||||
ost_audio_register_callback(audio_callback);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
#ifndef FS_TASK_H
|
||||
#define FS_TASK_H
|
||||
|
||||
void fs_task_scan_index();
|
||||
#include <stdint.h>
|
||||
|
||||
typedef void (*fs_result_cb_t)(bool);
|
||||
|
||||
void fs_task_scan_index(fs_result_cb_t cb);
|
||||
void fs_task_initialize();
|
||||
void fs_task_load_story(uint8_t *mem);
|
||||
void fs_task_media_start(char *image, char *sound);
|
||||
void fs_task_image_start(char *image);
|
||||
void fs_task_sound_start(char *sound);
|
||||
void fs_task_play_index();
|
||||
void fs_task_read_block(uint32_t addr, uint8_t *block, fs_result_cb_t cb);
|
||||
|
||||
#endif // FS_TASK_H
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "system.h"
|
||||
#include "vm_task.h"
|
||||
#include "fs_task.h"
|
||||
#include "tusb.h"
|
||||
|
||||
// ===========================================================================================================
|
||||
// DEFINITIONS
|
||||
|
|
@ -63,51 +64,55 @@ void HmiTask(void *args)
|
|||
|
||||
ost_hmi_event_t *e = NULL;
|
||||
|
||||
// filesystem_display_image("/ba869e4b-03d6-4249-9202-85b4cec767a7/images/bird.qoi");
|
||||
|
||||
// Start by scanning the index file
|
||||
// fs_task_scan_index();
|
||||
// init device stack on configured roothub port
|
||||
tusb_init();
|
||||
|
||||
while (1)
|
||||
{
|
||||
uint32_t res = qor_mbox_wait(&HmiMailBox, (void **)&e, 1000);
|
||||
|
||||
if (res == QOR_MBOX_OK)
|
||||
{
|
||||
switch (OstState)
|
||||
{
|
||||
case OST_SYS_PLAY_STORY_TITLE:
|
||||
break;
|
||||
|
||||
case OST_SYS_WAIT_USER_EVENT:
|
||||
break;
|
||||
|
||||
case OST_SYS_WAIT_INDEX:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_printf("H"); // pour le debug only
|
||||
}
|
||||
// tud_task(); // tinyusb device task
|
||||
qor_sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
void hmi_task_ost_ready(uint32_t number_of_stories)
|
||||
{
|
||||
static ost_hmi_event_t OsReadyEv = {
|
||||
.ev = OST_SYS_PLAY_STORY_TITLE};
|
||||
|
||||
OstContext.number_of_stories = number_of_stories;
|
||||
OstContext.current_story = 0;
|
||||
qor_mbox_notify(&HmiMailBox, (void **)&OsReadyEv, QOR_MBOX_OPTION_SEND_BACK);
|
||||
}
|
||||
#include "msc_disk.h"
|
||||
|
||||
void hmi_task_initialize()
|
||||
{
|
||||
OstState = OST_SYS_WAIT_INDEX;
|
||||
qor_mbox_init(&HmiMailBox, (void **)&HmiQueue, 10);
|
||||
|
||||
msc_disk_initialize();
|
||||
|
||||
qor_create_thread(&HmiTcb, HmiTask, HmiStack, sizeof(HmiStack) / sizeof(HmiStack[0]), HMI_TASK_PRIORITY, "HmiTask"); // less priority is the HMI (user inputs and LCD)
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Device callbacks
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
// blink_interval_ms = BLINK_MOUNTED;
|
||||
}
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void)
|
||||
{
|
||||
// blink_interval_ms = BLINK_NOT_MOUNTED;
|
||||
}
|
||||
|
||||
// Invoked when usb bus is suspended
|
||||
// remote_wakeup_en : if host allow us to perform remote wakeup
|
||||
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
|
||||
void tud_suspend_cb(bool remote_wakeup_en)
|
||||
{
|
||||
(void)remote_wakeup_en;
|
||||
// blink_interval_ms = BLINK_SUSPENDED;
|
||||
}
|
||||
|
||||
// Invoked when usb bus is resumed
|
||||
void tud_resume_cb(void)
|
||||
{
|
||||
// blink_interval_ms = BLINK_MOUNTED;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -260,10 +260,23 @@ bool __attribute__((naked)) qor_start(qor_tcb_t *idle_tcb, thread_func_t idle_ta
|
|||
|
||||
qor_create_thread(idle_tcb, idle_task, idle_stack, idle_stack_size, 0, "IdleTask");
|
||||
|
||||
// FIXME: use the scheduler to find the best first thread to start
|
||||
IdleTcb = idle_tcb;
|
||||
IdleTask = idle_task;
|
||||
RunPt = TcbHead;
|
||||
RunPt = IdleTcb;
|
||||
|
||||
// Find the best first thread to start (highest priority)
|
||||
qor_tcb_t *t = TcbHead;
|
||||
if (t != NULL)
|
||||
{
|
||||
while (t->next != NULL)
|
||||
{
|
||||
if (t->priority > RunPt->priority)
|
||||
{
|
||||
RunPt = t;
|
||||
}
|
||||
t = t->next;
|
||||
}
|
||||
}
|
||||
|
||||
timer_init();
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,10 @@ typedef enum
|
|||
{
|
||||
VM_EV_NO_EVENT = 0,
|
||||
VM_EV_START_STORY_EVENT = 0xA1,
|
||||
VM_EV_BUTTON_EVENT = 0x88
|
||||
VM_EV_EXEC_HOME_INDEX = 0xB5,
|
||||
VM_EV_BUTTON_EVENT = 0x88,
|
||||
VM_EV_END_OF_SOUND = 0x4E,
|
||||
VM_EV_ERROR = 0xE0
|
||||
} ost_vm_ev_type_t;
|
||||
typedef struct
|
||||
{
|
||||
|
|
@ -31,9 +34,12 @@ typedef struct
|
|||
|
||||
typedef enum
|
||||
{
|
||||
OST_VM_STATE_WAIT_EVENT,
|
||||
OST_VM_STATE_HOME,
|
||||
OST_VM_STATE_HOME_WAIT_FS,
|
||||
OST_VM_STATE_RUN_STORY,
|
||||
OST_VM_STATE_WAIT_BUTTON,
|
||||
OST_VM_STATE_ERROR //!< General error, cannot continue
|
||||
|
||||
} ost_vm_state_t;
|
||||
|
||||
// ===========================================================================================================
|
||||
|
|
@ -51,8 +57,6 @@ static char CurrentStory[260]; // Current story path
|
|||
static char ImageFile[260];
|
||||
static char SoundFile[260];
|
||||
|
||||
static ost_vm_state_t VmState = OST_VM_STATE_WAIT_EVENT;
|
||||
|
||||
// ===========================================================================================================
|
||||
// VIRTUAL MACHINE TASK
|
||||
// ===========================================================================================================
|
||||
|
|
@ -80,19 +84,13 @@ uint8_t vm_syscall(chip32_ctx_t *ctx, uint8_t code)
|
|||
// Media
|
||||
if (code == 1) // Execute media
|
||||
{
|
||||
char *image_ptr = NULL;
|
||||
char *sound_ptr = NULL;
|
||||
if (m_chip32_ctx.registers[R0] != 0)
|
||||
{
|
||||
// image file name address is in R0
|
||||
// QString imageFile = m_model.BuildFullImagePath(GetFileNameFromMemory(m_chip32_ctx.registers[R0]));
|
||||
// m_ostHmiDock->SetImage(imageFile);
|
||||
get_file_from_memory(ImageFile, m_chip32_ctx.registers[R0]);
|
||||
image_ptr = ImageFile;
|
||||
}
|
||||
else
|
||||
{
|
||||
// m_ostHmiDock->ClearImage();
|
||||
fs_task_image_start(ImageFile);
|
||||
}
|
||||
|
||||
if (m_chip32_ctx.registers[R1] != 0)
|
||||
|
|
@ -102,9 +100,8 @@ uint8_t vm_syscall(chip32_ctx_t *ctx, uint8_t code)
|
|||
// qDebug() << ", Sound: " << soundFile;
|
||||
// m_model.PlaySoundFile(soundFile);
|
||||
get_file_from_memory(SoundFile, m_chip32_ctx.registers[R1]);
|
||||
sound_ptr = SoundFile;
|
||||
fs_task_sound_start(SoundFile);
|
||||
}
|
||||
fs_task_media_start(image_ptr, sound_ptr);
|
||||
retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause
|
||||
}
|
||||
// WAIT EVENT bits:
|
||||
|
|
@ -135,6 +132,16 @@ static void button_callback(uint32_t flags)
|
|||
qor_mbox_notify(&VmMailBox, (void **)&ButtonEv, QOR_MBOX_OPTION_SEND_BACK);
|
||||
}
|
||||
|
||||
static void read_index_callback(bool success)
|
||||
{
|
||||
static ost_vm_event_t ReadIndexEv = {
|
||||
.ev = VM_EV_BUTTON_EVENT,
|
||||
.story_dir = NULL};
|
||||
|
||||
ReadIndexEv.ev = success ? VM_EV_EXEC_HOME_INDEX : VM_EV_ERROR;
|
||||
qor_mbox_notify(&VmMailBox, (void **)&ReadIndexEv, QOR_MBOX_OPTION_SEND_BACK);
|
||||
}
|
||||
|
||||
void VmTask(void *args)
|
||||
{
|
||||
// VM Initialize
|
||||
|
|
@ -151,57 +158,37 @@ void VmTask(void *args)
|
|||
m_chip32_ctx.syscall = vm_syscall;
|
||||
|
||||
chip32_result_t run_result;
|
||||
ost_vm_event_t *e = NULL;
|
||||
ost_vm_event_t *message = NULL;
|
||||
uint32_t res = 0;
|
||||
bool isHome = true;
|
||||
|
||||
ost_vm_state_t VmState = OST_VM_STATE_HOME;
|
||||
fs_task_scan_index(read_index_callback);
|
||||
isHome = true;
|
||||
|
||||
while (1)
|
||||
{
|
||||
res = qor_mbox_wait(&VmMailBox, (void **)&message, 300); // On devrait recevoir un message toutes les 3ms (durée d'envoi d'un buffer I2S)
|
||||
|
||||
switch (VmState)
|
||||
if (res == QOR_MBOX_OK)
|
||||
{
|
||||
case OST_VM_STATE_RUN_STORY:
|
||||
do
|
||||
switch (VmState)
|
||||
{
|
||||
run_result = chip32_step(&m_chip32_ctx);
|
||||
|
||||
} while (run_result == VM_OK);
|
||||
|
||||
// pour le moment, dans tous les cas on attend un événement
|
||||
// Que ce soit par erreur, ou l'attente un appui boutton...
|
||||
VmState = OST_VM_STATE_WAIT_EVENT;
|
||||
break;
|
||||
|
||||
case OST_VM_STATE_WAIT_EVENT:
|
||||
default:
|
||||
res = qor_mbox_wait(&VmMailBox, (void **)&e, 300); // On devrait recevoir un message toutes les 3ms (durée d'envoi d'un buffer I2S)
|
||||
|
||||
if (res == QOR_MBOX_OK)
|
||||
{
|
||||
|
||||
if (isHome)
|
||||
{
|
||||
if (e->ev == VM_EV_BUTTON_EVENT)
|
||||
{
|
||||
if ((e->button_mask & OST_BUTTON_OK) == OST_BUTTON_OK)
|
||||
{
|
||||
// Load story from disk
|
||||
debug_printf("Clicked OK\r\n");
|
||||
fs_task_load_story(m_rom_data);
|
||||
}
|
||||
}
|
||||
else if (e->ev == VM_EV_START_STORY_EVENT)
|
||||
{
|
||||
// Launch the execution of a story
|
||||
chip32_initialize(&m_chip32_ctx);
|
||||
VmState = OST_VM_STATE_RUN_STORY;
|
||||
}
|
||||
}
|
||||
else
|
||||
case OST_VM_STATE_HOME:
|
||||
switch (message->ev)
|
||||
{
|
||||
case VM_EV_EXEC_HOME_INDEX:
|
||||
// La lecture de l'index est terminée, on demande l'affichage des médias
|
||||
fs_task_play_index();
|
||||
break;
|
||||
case VM_EV_ERROR:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -213,12 +200,66 @@ void vm_task_start_story()
|
|||
qor_mbox_notify(&VmMailBox, (void **)&VmStartEvent, QOR_MBOX_OPTION_SEND_BACK);
|
||||
}
|
||||
|
||||
void vm_task_sound_finished()
|
||||
{
|
||||
static ost_vm_event_t VmEndOfSoundEvent;
|
||||
VmEndOfSoundEvent.ev = VM_EV_END_OF_SOUND;
|
||||
qor_mbox_notify(&VmMailBox, (void **)&VmEndOfSoundEvent, QOR_MBOX_OPTION_SEND_BACK);
|
||||
}
|
||||
|
||||
void vm_task_initialize()
|
||||
{
|
||||
VmState = OST_VM_STATE_WAIT_EVENT;
|
||||
|
||||
qor_mbox_init(&VmMailBox, (void **)&VmQueue, 10);
|
||||
qor_create_thread(&VmTcb, VmTask, VmStack, sizeof(VmStack) / sizeof(VmStack[0]), VM_TASK_PRIORITY, "VmTask");
|
||||
|
||||
ost_button_register_callback(button_callback);
|
||||
}
|
||||
|
||||
/*
|
||||
case OST_VM_STATE_RUN_STORY:
|
||||
do
|
||||
{
|
||||
run_result = chip32_step(&m_chip32_ctx);
|
||||
|
||||
} while (run_result == VM_OK);
|
||||
|
||||
// pour le moment, dans tous les cas on attend un événement
|
||||
// Que ce soit par erreur, ou l'attente un appui boutton...
|
||||
VmState = OST_VM_STATE_WAIT_BUTTON;
|
||||
break;
|
||||
|
||||
case OST_VM_STATE_ERROR:
|
||||
qor_sleep(20);
|
||||
break;
|
||||
case OST_VM_STATE_WAIT_BUTTON:
|
||||
default:
|
||||
|
||||
if (isHome)
|
||||
{
|
||||
if (message->ev == VM_EV_BUTTON_EVENT)
|
||||
{
|
||||
if ((message->button_mask & OST_BUTTON_OK) == OST_BUTTON_OK)
|
||||
{
|
||||
// Load story from disk
|
||||
debug_printf("Clicked OK\r\n");
|
||||
fs_task_load_story(m_rom_data);
|
||||
}
|
||||
}
|
||||
else if (message->ev == VM_EV_START_STORY_EVENT)
|
||||
{
|
||||
// Launch the execution of a story
|
||||
chip32_initialize(&m_chip32_ctx);
|
||||
isHome = false;
|
||||
VmState = OST_VM_STATE_RUN_STORY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// VM en cours d'exécution
|
||||
if (message->ev == VM_EV_END_OF_SOUND)
|
||||
{
|
||||
VmState = OST_VM_STATE_RUN_STORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
#ifndef VM_TASK_H
|
||||
#define VM_TASK_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef void (*vm_result_cb_t)(bool);
|
||||
|
||||
void vm_task_start_story();
|
||||
void vm_task_initialize();
|
||||
void vm_task_sound_finished();
|
||||
|
||||
#endif // VM_TASK_H
|
||||
|
|
|
|||
Loading…
Reference in a new issue