mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
firmware: put tasks into dedicated files with clean API, prepare story title image and sounds
This commit is contained in:
parent
cebd349af4
commit
084af988cb
21 changed files with 744 additions and 329 deletions
Binary file not shown.
BIN
art/pack/dragon.png
Normal file
BIN
art/pack/dragon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
|
|
@ -33,12 +33,14 @@ set(OST_SRCS
|
|||
system/sdcard.c
|
||||
system/ff_diskio_sdcard.c
|
||||
system/debug.c
|
||||
system/picture.c
|
||||
system/filesystem.c
|
||||
system/sdcard.c
|
||||
system/qor.c
|
||||
system/qor_armv6m.s
|
||||
system/audio_player.c
|
||||
system/vm_task.c
|
||||
system/hmi_task.c
|
||||
system/fs_task.c
|
||||
system/ff/ff.c
|
||||
system/ff/ffsystem.c
|
||||
system/ff/ff_stubs.c
|
||||
|
|
|
|||
|
|
@ -4,6 +4,19 @@
|
|||
#include "ost_hal.h"
|
||||
|
||||
#include "filesystem.h"
|
||||
#include "mini_qoi.h"
|
||||
|
||||
#ifdef OST_USE_FF_LIBRARY
|
||||
#include "ff.h"
|
||||
#include "diskio.h"
|
||||
typedef FIL file_t;
|
||||
#else
|
||||
|
||||
// Use standard library
|
||||
typedef FILE *file_t;
|
||||
typedef int FRESULT;
|
||||
#define F_OK
|
||||
#endif
|
||||
|
||||
// SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
|
||||
// 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
|
||||
|
|
@ -151,3 +164,130 @@ bool filesystem_read_index_file(ost_context_t *ctx)
|
|||
debug_printf("ERROR: index.ost not found\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
static mqoi_dec_t dec;
|
||||
static mqoi_desc_t desc;
|
||||
|
||||
static uint8_t bmpImage[256];
|
||||
|
||||
static color_t line[320];
|
||||
|
||||
file_t file_open(const char *filename)
|
||||
{
|
||||
#ifdef OST_USE_FF_LIBRARY
|
||||
file_t fil;
|
||||
FRESULT fr = f_open(&fil, filename, FA_READ);
|
||||
if (fr != FR_OK)
|
||||
{
|
||||
debug_printf("ERROR: f_open %d\n\r", (int)fr);
|
||||
}
|
||||
return fil;
|
||||
#else
|
||||
return fopen(filename, "r");
|
||||
#endif
|
||||
}
|
||||
|
||||
void filesystem_display_image(const char *filename)
|
||||
{
|
||||
file_t fil;
|
||||
unsigned int br;
|
||||
|
||||
fil = file_open(filename);
|
||||
uint32_t imgW, imgH;
|
||||
|
||||
mqoi_desc_init(&desc);
|
||||
|
||||
// 1. Read header
|
||||
f_read(&fil, &desc.magic[0], sizeof(mqoi_desc_t) - 1, &br);
|
||||
|
||||
uint8_t errn = mqoi_desc_verify(&desc, &imgW, &imgH);
|
||||
|
||||
if (errn)
|
||||
{
|
||||
debug_printf("Invalid image, code %d\n", errn);
|
||||
return;
|
||||
}
|
||||
|
||||
debug_printf("Image dimensions: %d, %d\n", imgW, imgH);
|
||||
|
||||
uint32_t start, end, pxCount = 0;
|
||||
|
||||
volatile mqoi_rgba_t *px;
|
||||
|
||||
mqoi_dec_init(&dec, imgW * imgH);
|
||||
|
||||
// Serial.println("starting decode...");
|
||||
int index = 256; // force refill first time
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
while (!mqoi_dec_done(&dec))
|
||||
{
|
||||
if (index >= sizeof(bmpImage))
|
||||
{
|
||||
// refill buffer
|
||||
f_read(&fil, bmpImage, sizeof(bmpImage), &br);
|
||||
index = 0;
|
||||
}
|
||||
|
||||
mqoi_dec_push(&dec, bmpImage[index++]);
|
||||
|
||||
while ((px = mqoi_dec_pop(&dec)) != NULL)
|
||||
{
|
||||
pxCount++;
|
||||
|
||||
line[x].r = px->r;
|
||||
line[x].g = px->g;
|
||||
line[x].b = px->b;
|
||||
|
||||
x++;
|
||||
if (x >= 320)
|
||||
{
|
||||
ost_display_draw_h_line_rgb888(y, line);
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f_close(&fil);
|
||||
}
|
||||
|
||||
#define FS_MAX_SIZE_READ 256
|
||||
|
||||
void filesystem_load_rom(uint8_t *mem, const char *filename)
|
||||
{
|
||||
file_t fil;
|
||||
FILINFO fno;
|
||||
unsigned int br;
|
||||
|
||||
FRESULT fr = f_stat(filename, &fno);
|
||||
|
||||
if (fr == FR_OK)
|
||||
{
|
||||
int total_size = fno.fsize;
|
||||
fil = file_open(filename);
|
||||
int copied_size = 0;
|
||||
|
||||
do
|
||||
{
|
||||
int read_size = total_size > FS_MAX_SIZE_READ ? FS_MAX_SIZE_READ : total_size;
|
||||
|
||||
f_read(&fil, &mem[copied_size], read_size, &br);
|
||||
|
||||
if (br == read_size)
|
||||
{
|
||||
total_size -= read_size;
|
||||
copied_size += read_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_printf("Read file error\n");
|
||||
total_size = 0; // force exit
|
||||
return;
|
||||
}
|
||||
|
||||
} while (total_size > 0);
|
||||
|
||||
f_close(&fil);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,5 +11,7 @@ typedef struct
|
|||
|
||||
bool filesystem_read_index_file(ost_context_t *ctx);
|
||||
void filesystem_mount();
|
||||
void filesystem_display_image(const char *filename);
|
||||
void filesystem_load_rom(uint8_t *mem, const char *filename);
|
||||
|
||||
#endif // FILESYSTEM_H
|
||||
|
|
|
|||
192
software/system/fs_task.c
Normal file
192
software/system/fs_task.c
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
/**
|
||||
* @file fs_task.c
|
||||
*
|
||||
* @author your name (you@domain.com)
|
||||
* @brief
|
||||
* @version 0.1
|
||||
* @date 2023-07-29
|
||||
*
|
||||
* @copyright Copyright (c) 2023
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ost_hal.h"
|
||||
#include "debug.h"
|
||||
#include "qor.h"
|
||||
#include "audio_player.h"
|
||||
#include "filesystem.h"
|
||||
#include "system.h"
|
||||
#include "vm_task.h"
|
||||
#include "fs_task.h"
|
||||
|
||||
// ===========================================================================================================
|
||||
// DEFINITIONS
|
||||
// ===========================================================================================================
|
||||
typedef struct
|
||||
{
|
||||
uint8_t ev;
|
||||
} ost_audio_event_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FS_WAIT_FOR_EVENT,
|
||||
FS_PLAY_SOUND,
|
||||
FS_DISPLAY_IMAGE,
|
||||
FS_LOAD_INDEX,
|
||||
FS_LOAD_STORY
|
||||
} fs_state_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
fs_state_t ev;
|
||||
uint8_t *mem;
|
||||
char *filename;
|
||||
} ost_fs_event_t;
|
||||
|
||||
// ===========================================================================================================
|
||||
// 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;
|
||||
|
||||
// ===========================================================================================================
|
||||
// FILE SYSTEM TASK
|
||||
// ===========================================================================================================
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
void show_duration(uint32_t millisecondes)
|
||||
{
|
||||
uint32_t minutes, secondes, reste;
|
||||
|
||||
// Calcul des minutes, secondes et millisecondes
|
||||
minutes = millisecondes / (60 * 1000);
|
||||
reste = millisecondes % (60 * 1000);
|
||||
secondes = reste / 1000;
|
||||
reste = reste % 1000;
|
||||
|
||||
// Affichage du temps
|
||||
debug_printf("Temps : %d minutes, %d secondes, %d millisecondes\r\n", minutes, secondes, reste);
|
||||
}
|
||||
|
||||
void play_sound_file(const char *filename)
|
||||
{
|
||||
debug_printf("\r\n-------------------------------------------------------\r\nPlaying: out2.wav\r\n");
|
||||
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);
|
||||
}
|
||||
|
||||
void FsTask(void *args)
|
||||
{
|
||||
ost_fs_event_t *fs_ev = NULL;
|
||||
uint32_t res = 0;
|
||||
while (1)
|
||||
{
|
||||
switch (FsState)
|
||||
{
|
||||
FS_PLAY_SOUND:
|
||||
play_sound_file(fs_ev->filename);
|
||||
FsState = FS_WAIT_FOR_EVENT;
|
||||
break;
|
||||
FS_DISPLAY_IMAGE:
|
||||
filesystem_display_image(fs_ev->filename);
|
||||
FsState = FS_WAIT_FOR_EVENT;
|
||||
break;
|
||||
FS_LOAD_INDEX:
|
||||
filesystem_read_index_file(&OstContext);
|
||||
FsState = FS_WAIT_FOR_EVENT;
|
||||
break;
|
||||
FS_LOAD_STORY:
|
||||
filesystem_load_rom(fs_ev->mem, fs_ev->filename);
|
||||
// ROM loaded, execute story
|
||||
FsState = FS_WAIT_FOR_EVENT;
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// for (;;)
|
||||
// {
|
||||
// ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 0);
|
||||
// qor_sleep(500);
|
||||
// ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 1);
|
||||
// qor_sleep(500);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
void fs_task_scan_index()
|
||||
{
|
||||
static ost_fs_event_t ScanIndexEv = {
|
||||
.ev = FS_LOAD_INDEX,
|
||||
.filename = NULL};
|
||||
|
||||
qor_mbox_notify(&FsMailBox, (void **)&ScanIndexEv, 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);
|
||||
|
||||
qor_create_thread(&FsTcb, FsTask, FsStack, sizeof(FsStack) / sizeof(FsStack[0]), FS_TASK_PRIORITY, "FsTask");
|
||||
}
|
||||
5
software/system/fs_task.h
Normal file
5
software/system/fs_task.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#ifndef FS_TASK_H
|
||||
#define FS_TASK_H
|
||||
|
||||
void fs_task_initialize();
|
||||
#endif // FS_TASK_H
|
||||
78
software/system/hmi_task.c
Normal file
78
software/system/hmi_task.c
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* @file hmi_task.c
|
||||
*
|
||||
* @author your name (you@domain.com)
|
||||
* @brief
|
||||
* @version 0.1
|
||||
* @date 2023-07-29
|
||||
*
|
||||
* @copyright Copyright (c) 2023
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ost_hal.h"
|
||||
#include "debug.h"
|
||||
#include "qor.h"
|
||||
#include "audio_player.h"
|
||||
#include "filesystem.h"
|
||||
#include "system.h"
|
||||
#include "vm_task.h"
|
||||
#include "fs_task.h"
|
||||
|
||||
// ===========================================================================================================
|
||||
// DEFINITIONS
|
||||
// ===========================================================================================================
|
||||
typedef struct
|
||||
{
|
||||
uint8_t ev;
|
||||
} ost_hmi_event_t;
|
||||
|
||||
// ===========================================================================================================
|
||||
// GLOBAL STORY VARIABLES
|
||||
// ===========================================================================================================
|
||||
|
||||
static qor_tcb_t HmiTcb;
|
||||
static uint32_t HmiStack[4096];
|
||||
|
||||
static qor_mbox_t HmiMailBox;
|
||||
|
||||
static ost_hmi_event_t HmiEvent;
|
||||
|
||||
static ost_hmi_event_t HmiQueue[10];
|
||||
|
||||
// ===========================================================================================================
|
||||
// HMI TASK (user interface, buttons manager, LCD)
|
||||
// ===========================================================================================================
|
||||
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
|
||||
|
||||
while (1)
|
||||
{
|
||||
uint32_t res = qor_mbox_wait(&HmiMailBox, (void **)&e, 1000);
|
||||
|
||||
if (res == QOR_MBOX_OK)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_printf("H"); // pour le debug only
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hmi_task_initialize()
|
||||
{
|
||||
qor_mbox_init(&HmiMailBox, (void **)&HmiQueue, 10);
|
||||
|
||||
qor_create_thread(&HmiTcb, HmiTask, HmiStack, sizeof(HmiStack) / sizeof(HmiStack[0]), HMI_TASK_PRIORITY, "HmiTask"); // less priority is the HMI (user inputs and LCD)
|
||||
}
|
||||
6
software/system/hmi_task.h
Normal file
6
software/system/hmi_task.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef HMI_TASK_H
|
||||
#define HMI_TASK_H
|
||||
|
||||
void hmi_task_initialize();
|
||||
|
||||
#endif // HMI_TASK_H
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
#include "ost_hal.h"
|
||||
#include "debug.h"
|
||||
#include "filesystem.h"
|
||||
#include "picture.h"
|
||||
|
||||
#include "qor.h"
|
||||
#include "rotary-button.h"
|
||||
|
||||
|
|
@ -12,331 +12,16 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "audio_player.h"
|
||||
#include "chip32_vm.h"
|
||||
#include "mini_qoi.h"
|
||||
|
||||
#ifdef OST_USE_FF_LIBRARY
|
||||
#include "ff.h"
|
||||
#include "diskio.h"
|
||||
typedef FIL file_t;
|
||||
#else
|
||||
|
||||
// Use standard library
|
||||
typedef FILE *file_t;
|
||||
typedef int FRESULT;
|
||||
#define F_OK
|
||||
#endif
|
||||
|
||||
file_t file_open(const char *filename)
|
||||
{
|
||||
#ifdef OST_USE_FF_LIBRARY
|
||||
file_t fil;
|
||||
FRESULT fr = f_open(&fil, filename, FA_READ);
|
||||
if (fr != FR_OK)
|
||||
{
|
||||
debug_printf("ERROR: f_open %d\n\r", (int)fr);
|
||||
}
|
||||
return fil;
|
||||
#else
|
||||
return fopen(filename, "r");
|
||||
#endif
|
||||
}
|
||||
#include "system.h"
|
||||
#include "hmi_task.h"
|
||||
#include "vm_task.h"
|
||||
#include "fs_task.h"
|
||||
|
||||
void ost_hal_panic()
|
||||
{
|
||||
}
|
||||
|
||||
// ===========================================================================================================
|
||||
// GLOBAL STORY VARIABLES
|
||||
// ===========================================================================================================
|
||||
static ost_context_t OstContext;
|
||||
|
||||
static mqoi_dec_t dec;
|
||||
static mqoi_desc_t desc;
|
||||
/*
|
||||
if (pixel == info_header.width)
|
||||
{
|
||||
// enough pixels to write a line to the screen
|
||||
ost_display_draw_h_line(pos.y, decompressed, palette);
|
||||
// debug_printf("POS Y: %d", pos.y);
|
||||
|
||||
memset(decompressed, 0, sizeof(decompressed));
|
||||
// ili9341_write(&pos, decompressed);
|
||||
// next line...
|
||||
pos.y++;
|
||||
totalPixels += info_header.width;
|
||||
pixel = 0;
|
||||
nblines++;
|
||||
}
|
||||
*/
|
||||
|
||||
static uint8_t bmpImage[256];
|
||||
|
||||
static color_t line[320];
|
||||
|
||||
void display_image(const char *filename)
|
||||
{
|
||||
file_t fil;
|
||||
unsigned int br;
|
||||
|
||||
fil = file_open(filename);
|
||||
uint32_t imgW, imgH;
|
||||
|
||||
mqoi_desc_init(&desc);
|
||||
|
||||
// 1. Read header
|
||||
f_read(&fil, &desc.magic[0], sizeof(mqoi_desc_t) - 1, &br);
|
||||
|
||||
uint8_t errn = mqoi_desc_verify(&desc, &imgW, &imgH);
|
||||
|
||||
if (errn)
|
||||
{
|
||||
debug_printf("Invalid image, code %d\n", errn);
|
||||
return;
|
||||
}
|
||||
|
||||
debug_printf("Image dimensions: %d, %d\n", imgW, imgH);
|
||||
|
||||
uint32_t start, end, pxCount = 0;
|
||||
|
||||
volatile mqoi_rgba_t *px;
|
||||
|
||||
mqoi_dec_init(&dec, imgW * imgH);
|
||||
|
||||
// Serial.println("starting decode...");
|
||||
int index = 256; // force refill first time
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
while (!mqoi_dec_done(&dec))
|
||||
{
|
||||
if (index >= sizeof(bmpImage))
|
||||
{
|
||||
// refill buffer
|
||||
f_read(&fil, bmpImage, sizeof(bmpImage), &br);
|
||||
index = 0;
|
||||
}
|
||||
|
||||
mqoi_dec_push(&dec, bmpImage[index++]);
|
||||
|
||||
while ((px = mqoi_dec_pop(&dec)) != NULL)
|
||||
{
|
||||
pxCount++;
|
||||
|
||||
line[x].r = px->r;
|
||||
line[x].g = px->g;
|
||||
line[x].b = px->b;
|
||||
|
||||
x++;
|
||||
if (x >= 320)
|
||||
{
|
||||
ost_display_draw_h_line_rgb888(y, line);
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f_close(&fil);
|
||||
}
|
||||
|
||||
// ===========================================================================================================
|
||||
// HMI TASK (user interface, buttons manager, LCD)
|
||||
// ===========================================================================================================
|
||||
static qor_tcb_t HmiTcb;
|
||||
static uint32_t HmiStack[4096];
|
||||
|
||||
static qor_mbox_t HmiMailBox;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t ev;
|
||||
} ost_hmi_event_t;
|
||||
|
||||
static ost_hmi_event_t HmiEvent;
|
||||
|
||||
ost_hmi_event_t HmiQueue[10];
|
||||
|
||||
void HmiTask(void *args)
|
||||
{
|
||||
qor_mbox_init(&HmiMailBox, (void **)&HmiQueue, 10);
|
||||
|
||||
ost_hmi_event_t *e = NULL;
|
||||
|
||||
filesystem_read_index_file(&OstContext);
|
||||
|
||||
display_image("/ba869e4b-03d6-4249-9202-85b4cec767a7/images/bird.qoi");
|
||||
|
||||
while (1)
|
||||
{
|
||||
uint32_t res = qor_mbox_wait(&HmiMailBox, (void **)&e, 1000);
|
||||
|
||||
if (res == QOR_MBOX_OK)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_printf("H"); // pour le debug only
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================================================
|
||||
// VIRTUAL MACHINE TASK
|
||||
// ===========================================================================================================
|
||||
static qor_tcb_t VmTcb;
|
||||
static uint32_t VmStack[4096];
|
||||
|
||||
static qor_mbox_t VmMailBox;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t ev;
|
||||
} ost_vm_event_t;
|
||||
ost_vm_event_t VmQueue[10];
|
||||
|
||||
static ost_vm_event_t VmEvent;
|
||||
|
||||
static uint8_t m_rom_data[16 * 1024];
|
||||
static uint8_t m_ram_data[16 * 1024];
|
||||
static chip32_ctx_t m_chip32_ctx;
|
||||
|
||||
// Index file parameter, reference an index in the file
|
||||
|
||||
uint8_t vm_syscall(chip32_ctx_t *ctx, uint8_t signum)
|
||||
{
|
||||
}
|
||||
|
||||
void VmTask(void *args)
|
||||
{
|
||||
// VM Initialize
|
||||
m_chip32_ctx.stack_size = 512;
|
||||
|
||||
m_chip32_ctx.rom.mem = m_rom_data;
|
||||
m_chip32_ctx.rom.addr = 0;
|
||||
m_chip32_ctx.rom.size = sizeof(m_rom_data);
|
||||
|
||||
m_chip32_ctx.ram.mem = m_ram_data;
|
||||
m_chip32_ctx.ram.addr = sizeof(m_rom_data);
|
||||
m_chip32_ctx.ram.size = sizeof(m_ram_data);
|
||||
|
||||
m_chip32_ctx.syscall = vm_syscall;
|
||||
|
||||
chip32_initialize(&m_chip32_ctx);
|
||||
|
||||
qor_mbox_init(&VmMailBox, (void **)&VmQueue, 10);
|
||||
|
||||
chip32_result_t run_result;
|
||||
ost_vm_event_t *e = NULL;
|
||||
|
||||
while (1)
|
||||
{
|
||||
uint32_t 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 (VmEvent.ev == 1)
|
||||
{
|
||||
do
|
||||
{
|
||||
run_result = chip32_step(&m_chip32_ctx);
|
||||
} while (run_result != VM_OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================================================
|
||||
// AUDIO TASK
|
||||
// ===========================================================================================================
|
||||
static qor_tcb_t AudioTcb;
|
||||
static uint32_t AudioStack[4096];
|
||||
|
||||
static qor_mbox_t AudioMailBox;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t ev;
|
||||
} ost_audio_event_t;
|
||||
|
||||
static ost_audio_event_t wake_up;
|
||||
|
||||
ost_audio_event_t audio_queue[10];
|
||||
|
||||
static int dbg_state = 0;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
void show_duration(uint32_t millisecondes)
|
||||
{
|
||||
uint32_t minutes, secondes, reste;
|
||||
|
||||
// Calcul des minutes, secondes et millisecondes
|
||||
minutes = millisecondes / (60 * 1000);
|
||||
reste = millisecondes % (60 * 1000);
|
||||
secondes = reste / 1000;
|
||||
reste = reste % 1000;
|
||||
|
||||
// Affichage du temps
|
||||
debug_printf("Temps : %d minutes, %d secondes, %d millisecondes\r\n", minutes, secondes, reste);
|
||||
}
|
||||
|
||||
void AudioTask(void *args)
|
||||
{
|
||||
// picture_show("example.bmp");
|
||||
|
||||
wake_up.ev = 34;
|
||||
|
||||
qor_mbox_init(&AudioMailBox, (void **)&audio_queue, 10);
|
||||
|
||||
ost_audio_register_callback(audio_callback);
|
||||
|
||||
static bool onetime = true;
|
||||
|
||||
while (1)
|
||||
{
|
||||
debug_printf("\r\n-------------------------------------------------------\r\nPlaying: out2.wav\r\n");
|
||||
ost_system_stopwatch_start();
|
||||
ost_audio_play("out2.wav");
|
||||
|
||||
ost_audio_event_t *e = NULL;
|
||||
|
||||
int isPlaying = 0;
|
||||
int count = 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);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 0);
|
||||
qor_sleep(500);
|
||||
ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 1);
|
||||
qor_sleep(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================================================
|
||||
// IDLE TASK
|
||||
// ===========================================================================================================
|
||||
|
|
@ -369,9 +54,9 @@ int main()
|
|||
qor_init(125000000UL);
|
||||
|
||||
// 5. Initialize the tasks
|
||||
qor_create_thread(&HmiTcb, HmiTask, HmiStack, sizeof(HmiStack) / sizeof(HmiStack[0]), 1, "HmiTask"); // less priority is the HMI (user inputs and LCD)
|
||||
qor_create_thread(&VmTcb, VmTask, VmStack, sizeof(VmStack) / sizeof(VmStack[0]), 2, "VmTask");
|
||||
qor_create_thread(&AudioTcb, AudioTask, AudioStack, sizeof(AudioStack) / sizeof(AudioStack[0]), 3, "AudioTask"); ///< High priority for audio
|
||||
hmi_task_initialize();
|
||||
vm_task_initialize();
|
||||
fs_task_initialize();
|
||||
|
||||
// 6. Start the operating system!
|
||||
qor_start(&IdleTcb, IdleTask, IdleStack, 1024);
|
||||
|
|
|
|||
9
software/system/system.h
Normal file
9
software/system/system.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef SYSTEM_H
|
||||
#define SYSTEM_H
|
||||
|
||||
// On regroupe ici au les priorités des différents threads afin d'avoir une vision plus large
|
||||
#define HMI_TASK_PRIORITY 1
|
||||
#define VM_TASK_PRIORITY 2
|
||||
#define FS_TASK_PRIORITY 3 ///< High priority for audio / file system access
|
||||
|
||||
#endif // SYSTEM_H
|
||||
156
software/system/vm_task.c
Normal file
156
software/system/vm_task.c
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ost_hal.h"
|
||||
#include "debug.h"
|
||||
#include "qor.h"
|
||||
#include "chip32_vm.h"
|
||||
#include "system.h"
|
||||
#include "vm_task.h"
|
||||
#include "fs_task.h"
|
||||
|
||||
// ===========================================================================================================
|
||||
// DEFINITIONS
|
||||
// ===========================================================================================================
|
||||
#define VM_EV_NO_EVENT 0
|
||||
#define VM_EV_START_STORY_EVENT 0xA1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t ev;
|
||||
const char *story_dir;
|
||||
} ost_vm_event_t;
|
||||
|
||||
// ===========================================================================================================
|
||||
// PRIVATE GLOBAL VARIABLES
|
||||
// ===========================================================================================================
|
||||
static qor_tcb_t VmTcb;
|
||||
static uint32_t VmStack[4096];
|
||||
static qor_mbox_t VmMailBox;
|
||||
static ost_vm_event_t VmQueue[10];
|
||||
|
||||
static ost_vm_event_t VmStartEvent;
|
||||
static uint8_t m_rom_data[16 * 1024];
|
||||
static uint8_t m_ram_data[16 * 1024];
|
||||
static chip32_ctx_t m_chip32_ctx;
|
||||
static char CurrentStory[260]; // Current story path
|
||||
static char ImageFile[260];
|
||||
static char SoundFile[260];
|
||||
|
||||
// ===========================================================================================================
|
||||
// VIRTUAL MACHINE TASK
|
||||
// ===========================================================================================================
|
||||
|
||||
void get_file_from_memory(char *mem, uint32_t addr)
|
||||
{
|
||||
bool isRam = addr & 0x80000000;
|
||||
addr &= 0xFFFF; // mask the RAM/ROM bit, ensure 16-bit addressing
|
||||
if (isRam)
|
||||
{
|
||||
strcpy(&mem[0], (const char *)&m_chip32_ctx.ram.mem[addr]);
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(&mem[0], (const char *)&m_chip32_ctx.rom.mem[addr]);
|
||||
}
|
||||
}
|
||||
|
||||
// Callback from the VM
|
||||
// Called inside the thread context
|
||||
uint8_t vm_syscall(chip32_ctx_t *ctx, uint8_t code)
|
||||
{
|
||||
uint8_t retCode = SYSCALL_RET_OK;
|
||||
|
||||
// Media
|
||||
if (code == 1) // Execute media
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
// m_ostHmiDock->ClearImage();
|
||||
}
|
||||
|
||||
if (m_chip32_ctx.registers[R1] != 0)
|
||||
{
|
||||
// sound file name address is in R1
|
||||
// QString soundFile = m_model.BuildFullSoundPath(GetFileNameFromMemory(m_chip32_ctx.registers[R1]));
|
||||
// qDebug() << ", Sound: " << soundFile;
|
||||
// m_model.PlaySoundFile(soundFile);
|
||||
}
|
||||
retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause
|
||||
}
|
||||
// WAIT EVENT bits:
|
||||
// 0: block
|
||||
// 1: OK button
|
||||
// 2: home button
|
||||
// 3: pause button
|
||||
// 4: rotary left
|
||||
// 5: rotary right
|
||||
else if (code == 2) // Wait for event
|
||||
{
|
||||
// Event mask is located in R0
|
||||
// optional timeout is located in R1
|
||||
// if timeout is set to zero, wait for infinite and beyond
|
||||
retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause
|
||||
}
|
||||
|
||||
return retCode;
|
||||
}
|
||||
|
||||
void VmTask(void *args)
|
||||
{
|
||||
// VM Initialize
|
||||
m_chip32_ctx.stack_size = 512;
|
||||
|
||||
m_chip32_ctx.rom.mem = m_rom_data;
|
||||
m_chip32_ctx.rom.addr = 0;
|
||||
m_chip32_ctx.rom.size = sizeof(m_rom_data);
|
||||
|
||||
m_chip32_ctx.ram.mem = m_ram_data;
|
||||
m_chip32_ctx.ram.addr = sizeof(m_rom_data);
|
||||
m_chip32_ctx.ram.size = sizeof(m_ram_data);
|
||||
|
||||
m_chip32_ctx.syscall = vm_syscall;
|
||||
|
||||
chip32_result_t run_result;
|
||||
ost_vm_event_t *e = NULL;
|
||||
|
||||
while (1)
|
||||
{
|
||||
uint32_t 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 (VmStartEvent.ev == VM_EV_START_STORY_EVENT)
|
||||
{
|
||||
// Launch the execution of a story
|
||||
chip32_initialize(&m_chip32_ctx);
|
||||
|
||||
do
|
||||
{
|
||||
run_result = chip32_step(&m_chip32_ctx);
|
||||
} while (run_result != VM_OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vm_task_start_story(const char *story_directory)
|
||||
{
|
||||
VmStartEvent.story_dir = story_directory;
|
||||
qor_mbox_notify(&VmMailBox, (void **)&VmStartEvent, QOR_MBOX_OPTION_SEND_BACK);
|
||||
}
|
||||
|
||||
void vm_task_initialize()
|
||||
{
|
||||
VmStartEvent.ev = VM_EV_START_STORY_EVENT;
|
||||
qor_mbox_init(&VmMailBox, (void **)&VmQueue, 10);
|
||||
qor_create_thread(&VmTcb, VmTask, VmStack, sizeof(VmStack) / sizeof(VmStack[0]), VM_TASK_PRIORITY, "VmTask");
|
||||
}
|
||||
7
software/system/vm_task.h
Normal file
7
software/system/vm_task.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef VM_TASK_H
|
||||
#define VM_TASK_H
|
||||
|
||||
void vm_task_start_story(const char *story_directory);
|
||||
void vm_task_initialize();
|
||||
|
||||
#endif // VM_TASK_H
|
||||
|
|
@ -202,6 +202,34 @@ MainWindow::MainWindow()
|
|||
}
|
||||
});
|
||||
|
||||
connect(m_resourcesDock, &ResourcesDock::sigChooseTitle, [&](bool isImage) {
|
||||
m_chooseFileUi.tableView->setModel(&m_resourcesDock->getModel());
|
||||
m_chooseFileDialog->exec();
|
||||
|
||||
// Get the file choosen
|
||||
QModelIndexList selection = m_chooseFileUi.tableView->selectionModel()->selectedRows();
|
||||
|
||||
if (selection.count() > 0)
|
||||
{
|
||||
// Take first
|
||||
QModelIndex index = selection.at(0);
|
||||
Resource res;
|
||||
if (m_project.GetResourceAt(index.row(), res))
|
||||
{
|
||||
if (isImage)
|
||||
{
|
||||
m_project.SetTitleImage(res.file);
|
||||
m_resourcesDock->SetTitleImage(res.file.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_project.SetTitleSound(res.file);
|
||||
m_resourcesDock->SetTitleSound(res.file.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_toolbar, &ToolBar::sigNew, this, [&]() {
|
||||
NewProject();
|
||||
});
|
||||
|
|
@ -366,7 +394,7 @@ void MainWindow::BuildAll()
|
|||
// 2. Compile the assembly to machine binary
|
||||
GenerateBinary();
|
||||
|
||||
// 3. Conert all media
|
||||
// 3. Convert all media to desired type format
|
||||
ConvertResources();
|
||||
}
|
||||
|
||||
|
|
@ -422,6 +450,9 @@ void MainWindow::ExitProgram()
|
|||
void MainWindow::RefreshProjectInformation()
|
||||
{
|
||||
setWindowTitle(QString("StoryTeller Editor - ") + m_project.GetProjectFilePath().c_str());
|
||||
|
||||
m_resourcesDock->SetTitleImage(m_project.GetTitleImage().c_str());
|
||||
m_resourcesDock->SetTitleSound(m_project.GetTitleSound().c_str());
|
||||
}
|
||||
|
||||
void MainWindow::MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@
|
|||
<string>Story Resources</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="addSoundButton">
|
||||
|
|
@ -40,7 +40,57 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Title image</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="titleImage">
|
||||
<property name="text">
|
||||
<string><no file !></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="chooseTitleImageButton">
|
||||
<property name="text">
|
||||
<string>Choose file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Title sound</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="titleSound">
|
||||
<property name="text">
|
||||
<string><no file !></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="chooseTitleSoundButton">
|
||||
<property name="text">
|
||||
<string>Choose file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableView" name="resourcesView">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
|
|
|
|||
|
|
@ -67,6 +67,24 @@ ResourcesDock::ResourcesDock(StoryProject &project, ResourceModel &model)
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_uiOstResources.chooseTitleImageButton, &QPushButton::clicked, [&](bool enable) {
|
||||
emit sigChooseTitle(true);
|
||||
});
|
||||
|
||||
connect(m_uiOstResources.chooseTitleSoundButton, &QPushButton::clicked, [&](bool enable) {
|
||||
emit sigChooseTitle(false);
|
||||
});
|
||||
}
|
||||
|
||||
void ResourcesDock::SetTitleImage(const QString &name)
|
||||
{
|
||||
m_uiOstResources.titleImage->setText(name);
|
||||
}
|
||||
|
||||
void ResourcesDock::SetTitleSound(const QString &name)
|
||||
{
|
||||
m_uiOstResources.titleSound->setText(name);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,12 @@ public:
|
|||
|
||||
void SetFilterType(const QString &type) { m_proxyModel.setFilterType(type); }
|
||||
|
||||
void SetTitleImage(const QString &name);
|
||||
void SetTitleSound(const QString &name);
|
||||
|
||||
signals:
|
||||
void sigChooseTitle(bool isImage);
|
||||
|
||||
private:
|
||||
StoryProject &m_project;
|
||||
Ui::ostResources m_uiOstResources;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ void StoryProject::SaveStory(const std::vector<uint8_t> &m_program)
|
|||
std::ofstream o(m_working_dir + std::filesystem::path::preferred_separator + "story.c32", std::ios::out | std::ios::binary);
|
||||
o.write(reinterpret_cast<const char*>(m_program.data()), m_program.size());
|
||||
o.close();
|
||||
|
||||
// Generate title files
|
||||
std::ofstream index(m_working_dir + std::filesystem::path::preferred_separator + "index.ost");
|
||||
index << "/" << m_uuid << "/images/" << m_titleImage << "\n";
|
||||
index << "/" << m_uuid << "/sounds/" << m_titleSound << "\n";
|
||||
index.close();
|
||||
|
||||
}
|
||||
|
||||
void StoryProject::Initialize(const std::string &file_path)
|
||||
|
|
@ -63,6 +70,8 @@ bool StoryProject::Load(const std::string &file_path, nlohmann::json &model)
|
|||
|
||||
m_name = projectData["name"].get<std::string>();
|
||||
m_uuid = projectData["uuid"].get<std::string>();
|
||||
m_titleImage = projectData.value("title_image", "");
|
||||
m_titleSound = projectData.value("title_sound", "");
|
||||
|
||||
if (j.contains("resources"))
|
||||
{
|
||||
|
|
@ -159,7 +168,7 @@ bool StoryProject::Load(const std::string &file_path, nlohmann::json &model)
|
|||
void StoryProject::Save(const nlohmann::json &model)
|
||||
{
|
||||
nlohmann::json j;
|
||||
j["project"] = { {"name", m_name}, {"uuid", m_uuid} };
|
||||
j["project"] = { {"name", m_name}, {"uuid", m_uuid}, { "title_image", m_titleImage }, { "title_sound", m_titleSound } };
|
||||
|
||||
{
|
||||
nlohmann::json resourcesData;
|
||||
|
|
@ -267,6 +276,16 @@ std::string StoryProject::FileToConstant(const std::string &FileName, const std:
|
|||
return "$" + f + " DC8 \"" + f + extension + "\", 8\r\n";
|
||||
}
|
||||
|
||||
void StoryProject::SetTitleImage(const std::string &titleImage)
|
||||
{
|
||||
m_titleImage = titleImage;
|
||||
}
|
||||
|
||||
void StoryProject::SetTitleSound(const std::string &titleSound)
|
||||
{
|
||||
m_titleSound = titleSound;
|
||||
}
|
||||
|
||||
void StoryProject::AppendResource(const Resource &res)
|
||||
{
|
||||
m_resources.push_back(res);
|
||||
|
|
|
|||
|
|
@ -88,6 +88,12 @@ struct StoryProject
|
|||
static void ReplaceCharacter(std::string &theString, const std::string &toFind, const std::string &toReplace);
|
||||
static std::string FileToConstant(const std::string &FileName, const std::string &extension);
|
||||
|
||||
void SetTitleImage(const std::string &titleImage);
|
||||
void SetTitleSound(const std::string &titleSound);
|
||||
|
||||
std::string GetTitleImage() const { return m_titleImage; }
|
||||
std::string GetTitleSound() const { return m_titleSound; }
|
||||
|
||||
// ------------- Resources Management
|
||||
void AppendResource(const Resource &res);
|
||||
bool GetResourceAt(int index, Resource &resOut);
|
||||
|
|
@ -117,6 +123,9 @@ private:
|
|||
std::filesystem::path m_soundsPath;
|
||||
bool m_initialized{false};
|
||||
|
||||
std::string m_titleImage;
|
||||
std::string m_titleSound;
|
||||
|
||||
std::vector<Resource> m_resources;
|
||||
std::string m_working_dir; /// Temporary folder based on the uuid, where the archive is unzipped
|
||||
std::string m_project_file_path; /// JSON project file
|
||||
|
|
|
|||
Loading…
Reference in a new issue