Output I2S audio with PIO/DMA
Requires a MAX98357A amplifier and a 2W 8ohms speaker
This commit is contained in:
+13
-13
@@ -1,24 +1,24 @@
|
|||||||
cmake_minimum_required(VERSION 3.13...3.23)
|
cmake_minimum_required(VERSION 3.13...3.23)
|
||||||
|
|
||||||
# initialize the SDK based on PICO_SDK_PATH
|
|
||||||
include(pico_sdk_import.cmake)
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
project(RP2040_GB C CXX)
|
project(RP2040_GB C CXX ASM)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
# Initialize the Raspberry Pi Pico SDK
|
|
||||||
pico_sdk_init()
|
pico_sdk_init()
|
||||||
|
|
||||||
add_executable(RP2040_GB src/main.c src/rom.c src/mk_ili9225.c)
|
add_executable(RP2040_GB
|
||||||
target_include_directories(RP2040_GB PRIVATE inc)
|
src/main.c
|
||||||
|
src/rom.c
|
||||||
|
src/mk_ili9225.c
|
||||||
|
ext/minigb_apu/minigb_apu.c
|
||||||
|
ext/i2s/i2s.c
|
||||||
|
)
|
||||||
|
|
||||||
SET(ENABLE_SOUND FALSE CACHE BOOL "Enable Sound emulation")
|
pico_generate_pio_header(RP2040_GB ${CMAKE_CURRENT_LIST_DIR}/ext/i2s/audio_i2s.pio)
|
||||||
if(ENABLE_SOUND)
|
|
||||||
ADD_COMPILE_DEFINITIONS(ENABLE_SOUND=1)
|
target_include_directories(RP2040_GB PRIVATE inc ext/minigb_apu ext/i2s)
|
||||||
target_sources(RP2040_GB PRIVATE ext/minigb_apu/minigb_apu.c)
|
|
||||||
target_include_directories(RP2040_GB PRIVATE ext/minigb_apu)
|
|
||||||
else()
|
|
||||||
ADD_COMPILE_DEFINITIONS(ENABLE_SOUND=0)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(RP2040_GB
|
target_link_libraries(RP2040_GB
|
||||||
pico_stdlib pico_stdio pico_bootrom pico_multicore pico_stdio pico_multicore
|
pico_stdlib pico_stdio pico_bootrom pico_multicore pico_stdio pico_multicore
|
||||||
|
|||||||
@@ -7,6 +7,16 @@ This fork includes all changes needed for the emulator to run on the [Pico-GB em
|
|||||||
RP2040-GB is a Game Boy (DMG) emulator [Peanut-GB](https://github.com/deltabeard/Peanut-GB) on the Raspberry Pi RP2040 microcontroller, using an ILI9225 screen.
|
RP2040-GB is a Game Boy (DMG) emulator [Peanut-GB](https://github.com/deltabeard/Peanut-GB) on the Raspberry Pi RP2040 microcontroller, using an ILI9225 screen.
|
||||||
Runs at more than 70 fps without audio emulation. With frame skip and interlacing, can run at up to 120 fps.
|
Runs at more than 70 fps without audio emulation. With frame skip and interlacing, can run at up to 120 fps.
|
||||||
|
|
||||||
|
# What you need
|
||||||
|
* Raspberry Pi Pico (1x)
|
||||||
|
* 2.2inch ILI9225 176×220 LCD Display Module (1x)
|
||||||
|
* MAX98357A amplifier (1x)
|
||||||
|
* 2W 8ohms speaker (1x)
|
||||||
|
* Micro Push Button Switch, Momentary Tactile Tact Touch, 6x6x6 mm, 4 pins (8x)
|
||||||
|
* Solderable Breadboard (1x)
|
||||||
|
* Dupont Wires Assorted Kit (Male to Female + Male to Male + Female to Female)
|
||||||
|
* Preformed Breadboard Jumper Wires
|
||||||
|
|
||||||
# Raspberry Pi Pico Pins Assignment
|
# Raspberry Pi Pico Pins Assignment
|
||||||
* UP = GP2
|
* UP = GP2
|
||||||
* DOWN = GP3
|
* DOWN = GP3
|
||||||
@@ -16,13 +26,15 @@ Runs at more than 70 fps without audio emulation. With frame skip and interlacin
|
|||||||
* BUTTON B = GP7
|
* BUTTON B = GP7
|
||||||
* SELECT = GP8
|
* SELECT = GP8
|
||||||
* START = GP9
|
* START = GP9
|
||||||
* BUZZER = GP15
|
|
||||||
* LCD CS = GP17
|
* LCD CS = GP17
|
||||||
* LCD CLK = GP18
|
* LCD CLK = GP18
|
||||||
* LCD SDI = GP19
|
* LCD SDI = GP19
|
||||||
* LCD RS = GP20
|
* LCD RS = GP20
|
||||||
* LCD RST = GP21
|
* LCD RST = GP21
|
||||||
* LCD LED = GP22
|
* LCD LED = GP22
|
||||||
|
* MAX98357A DIN = GP26
|
||||||
|
* MAX98357A BCLK = GP27
|
||||||
|
* MAX98357A LRC = GP28
|
||||||
|
|
||||||
# Installing
|
# Installing
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
;
|
||||||
|
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
;
|
||||||
|
; SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
;
|
||||||
|
|
||||||
|
; Transmit a mono or stereo I2S audio stream as stereo
|
||||||
|
; This is 16 bits per sample; can be altered by modifying the "set" params,
|
||||||
|
; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register.
|
||||||
|
;
|
||||||
|
; Autopull must be enabled, with threshold set to 32.
|
||||||
|
; Since I2S is MSB-first, shift direction should be to left.
|
||||||
|
; Hence the format of the FIFO word is:
|
||||||
|
;
|
||||||
|
; | 31 : 16 | 15 : 0 |
|
||||||
|
; | sample ws=0 | sample ws=1 |
|
||||||
|
;
|
||||||
|
; Data is output at 1 bit per clock. Use clock divider to adjust frequency.
|
||||||
|
; Fractional divider will probably be needed to get correct bit clock period,
|
||||||
|
; but for common syslck freqs this should still give a constant word select period.
|
||||||
|
;
|
||||||
|
; One output pin is used for the data output.
|
||||||
|
; Two side-set pins are used. Bit 0 is clock, bit 1 is word select.
|
||||||
|
|
||||||
|
; Send 16 bit words to the PIO for mono, 32 bit words for stereo
|
||||||
|
|
||||||
|
.program audio_i2s
|
||||||
|
.side_set 2
|
||||||
|
|
||||||
|
; /--- LRCLK
|
||||||
|
; |/-- BCLK
|
||||||
|
bitloop1: ; ||
|
||||||
|
out pins, 1 side 0b10
|
||||||
|
jmp x-- bitloop1 side 0b11
|
||||||
|
out pins, 1 side 0b00
|
||||||
|
set x, 14 side 0b01
|
||||||
|
|
||||||
|
bitloop0:
|
||||||
|
out pins, 1 side 0b00
|
||||||
|
jmp x-- bitloop0 side 0b01
|
||||||
|
out pins, 1 side 0b10
|
||||||
|
public entry_point:
|
||||||
|
set x, 14 side 0b11
|
||||||
|
|
||||||
|
% c-sdk {
|
||||||
|
|
||||||
|
static inline void audio_i2s_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) {
|
||||||
|
pio_sm_config sm_config = audio_i2s_program_get_default_config(offset);
|
||||||
|
|
||||||
|
sm_config_set_out_pins(&sm_config, data_pin, 1);
|
||||||
|
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
|
||||||
|
sm_config_set_out_shift(&sm_config, false, true, 32);
|
||||||
|
|
||||||
|
pio_sm_init(pio, sm, offset, &sm_config);
|
||||||
|
|
||||||
|
uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
|
||||||
|
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
|
||||||
|
pio_sm_set_pins(pio, sm, 0); // clear pins
|
||||||
|
|
||||||
|
pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_offset_entry_point));
|
||||||
|
}
|
||||||
|
|
||||||
|
%}
|
||||||
+100
@@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 Vincent Mistler
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
* deal in the Software without restriction, including without limitation the
|
||||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
* sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "i2s.h"
|
||||||
|
|
||||||
|
i2s_config_t i2s_get_default_config(void) {
|
||||||
|
i2s_config_t i2s_config = {
|
||||||
|
.sample_freq = 44100,
|
||||||
|
.channel_count = 2,
|
||||||
|
.data_pin = 26,
|
||||||
|
.clock_pin_base = 27,
|
||||||
|
.pio = pio0,
|
||||||
|
.sm = 0,
|
||||||
|
.dma_channel = 0,
|
||||||
|
.dma_buf = NULL,
|
||||||
|
.dma_trans_count = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
return i2s_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2s_init(i2s_config_t *i2s_config) {
|
||||||
|
uint8_t func=GPIO_FUNC_PIO0; // TODO: GPIO_FUNC_PIO0 for pio0 or GPIO_FUNC_PIO1 for pio1
|
||||||
|
gpio_set_function(i2s_config->data_pin, GPIO_FUNC_PIO0);
|
||||||
|
gpio_set_function(i2s_config->clock_pin_base, GPIO_FUNC_PIO0);
|
||||||
|
gpio_set_function(i2s_config->clock_pin_base+1, GPIO_FUNC_PIO0);
|
||||||
|
|
||||||
|
i2s_config->sm = pio_claim_unused_sm(i2s_config->pio, true);
|
||||||
|
|
||||||
|
uint offset = pio_add_program(i2s_config->pio, &audio_i2s_program);
|
||||||
|
|
||||||
|
audio_i2s_program_init(i2s_config->pio, i2s_config->sm , offset, i2s_config->data_pin , i2s_config->clock_pin_base);
|
||||||
|
|
||||||
|
// Set PIO clock
|
||||||
|
uint32_t system_clock_frequency = clock_get_hz(clk_sys);
|
||||||
|
uint32_t divider = system_clock_frequency * 4 / i2s_config->sample_freq; // avoid arithmetic overflow
|
||||||
|
pio_sm_set_clkdiv_int_frac(i2s_config->pio, i2s_config->sm , divider >> 8u, divider & 0xffu);
|
||||||
|
|
||||||
|
pio_sm_set_enabled(i2s_config->pio, i2s_config->sm, false);
|
||||||
|
|
||||||
|
// Allocate memory for the DMA buffer
|
||||||
|
i2s_config->dma_buf=malloc(i2s_config->dma_trans_count*sizeof(uint32_t));
|
||||||
|
|
||||||
|
// Direct Memory Access setup
|
||||||
|
i2s_config->dma_channel = dma_claim_unused_channel(true);
|
||||||
|
|
||||||
|
dma_channel_config dma_config = dma_channel_get_default_config(i2s_config->dma_channel);
|
||||||
|
channel_config_set_read_increment(&dma_config, true);
|
||||||
|
channel_config_set_write_increment(&dma_config, false);
|
||||||
|
channel_config_set_dreq(&dma_config, pio_get_dreq(i2s_config->pio, i2s_config->sm, true));
|
||||||
|
channel_config_set_transfer_data_size(&dma_config, DMA_SIZE_32);
|
||||||
|
dma_channel_configure(i2s_config->dma_channel,
|
||||||
|
&dma_config,
|
||||||
|
&(i2s_config->pio->txf[i2s_config->sm]), // Destination pointer
|
||||||
|
i2s_config->dma_buf, // Source pointer
|
||||||
|
i2s_config->dma_trans_count, // Number of 32 bits words to transfer
|
||||||
|
false // Start immediately
|
||||||
|
);
|
||||||
|
|
||||||
|
pio_sm_set_enabled(i2s_config->pio, i2s_config->sm , true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2s_write(const i2s_config_t *i2s_config,const int16_t *samples,const size_t len) {
|
||||||
|
for(size_t i=0;i<len;i++) {
|
||||||
|
pio_sm_put_blocking(i2s_config->pio, i2s_config->sm, (uint32_t)samples[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write samples to DMA buffer and initiate DMA transfer (non blocking)
|
||||||
|
void i2s_dma_write(i2s_config_t *i2s_config,const int16_t *samples) {
|
||||||
|
// Wait the completion of the previous DMA transfer
|
||||||
|
dma_channel_wait_for_finish_blocking(i2s_config->dma_channel);
|
||||||
|
// Copy samples into the DMA buffer
|
||||||
|
memcpy(i2s_config->dma_buf,samples,i2s_config->dma_trans_count*sizeof(int32_t));
|
||||||
|
// Initiate the DMA transfer
|
||||||
|
dma_channel_transfer_from_buffer_now(i2s_config->dma_channel,
|
||||||
|
i2s_config->dma_buf,
|
||||||
|
i2s_config->dma_trans_count);
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 Vincent Mistler
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
* deal in the Software without restriction, including without limitation the
|
||||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
* sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <hardware/pio.h>
|
||||||
|
#include <hardware/clocks.h>
|
||||||
|
#include <hardware/dma.h>
|
||||||
|
#include "audio_i2s.pio.h"
|
||||||
|
|
||||||
|
typedef struct i2s_config
|
||||||
|
{
|
||||||
|
uint32_t sample_freq;
|
||||||
|
uint16_t channel_count;
|
||||||
|
uint8_t data_pin;
|
||||||
|
uint8_t clock_pin_base;
|
||||||
|
PIO pio;
|
||||||
|
uint8_t sm;
|
||||||
|
uint8_t dma_channel;
|
||||||
|
uint16_t dma_trans_count;
|
||||||
|
uint16_t *dma_buf;
|
||||||
|
|
||||||
|
} i2s_config_t;
|
||||||
|
|
||||||
|
|
||||||
|
i2s_config_t i2s_get_default_config(void);
|
||||||
|
void i2s_init(i2s_config_t *i2s_config);
|
||||||
|
void i2s_write(const i2s_config_t *i2s_config,const int16_t *samples,const size_t len);
|
||||||
|
void i2s_dma_write(i2s_config_t *i2s_config,const int16_t *samples);
|
||||||
@@ -365,19 +365,17 @@ static void update_noise(int16_t *samples)
|
|||||||
/**
|
/**
|
||||||
* SDL2 style audio callback function.
|
* SDL2 style audio callback function.
|
||||||
*/
|
*/
|
||||||
void audio_callback(void *userdata, uint8_t *stream, int len)
|
void audio_callback(void *userdata, int16_t *stream, size_t len)
|
||||||
{
|
{
|
||||||
int16_t *samples = (int16_t *)stream;
|
|
||||||
|
|
||||||
/* Appease unused variable warning. */
|
/* Appease unused variable warning. */
|
||||||
(void)userdata;
|
(void)userdata;
|
||||||
|
|
||||||
memset(stream, 0, len);
|
memset(stream, 0, len);
|
||||||
|
|
||||||
update_square(samples, 0);
|
update_square(stream, 0);
|
||||||
update_square(samples, 1);
|
update_square(stream, 1);
|
||||||
update_wave(samples);
|
update_wave(stream);
|
||||||
update_noise(samples);
|
update_noise(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chan_trigger(uint_fast8_t i)
|
static void chan_trigger(uint_fast8_t i)
|
||||||
|
|||||||
@@ -9,19 +9,20 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define AUDIO_SAMPLE_RATE 32768
|
#define AUDIO_SAMPLE_RATE 44100
|
||||||
|
|
||||||
#define DMG_CLOCK_FREQ 4194304.0
|
#define DMG_CLOCK_FREQ 4194304.0
|
||||||
#define SCREEN_REFRESH_CYCLES 70224.0
|
#define SCREEN_REFRESH_CYCLES 70224.0
|
||||||
#define VERTICAL_SYNC (DMG_CLOCK_FREQ/SCREEN_REFRESH_CYCLES)
|
#define VERTICAL_SYNC (DMG_CLOCK_FREQ/SCREEN_REFRESH_CYCLES)
|
||||||
|
|
||||||
#define AUDIO_SAMPLES ((unsigned)(AUDIO_SAMPLE_RATE / VERTICAL_SYNC))
|
#define AUDIO_SAMPLES ((unsigned)(AUDIO_SAMPLE_RATE / VERTICAL_SYNC))
|
||||||
|
#define AUDIO_BUFFER_SIZE_BYTES (AUDIO_SAMPLES*4)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill allocated buffer "data" with "len" number of 32-bit floating point
|
* Fill allocated buffer "data" with "len" number of 32-bit floating point
|
||||||
* samples (native endian order) in stereo interleaved format.
|
* samples (native endian order) in stereo interleaved format.
|
||||||
*/
|
*/
|
||||||
void audio_callback(void *ptr, uint8_t *data, int len);
|
void audio_callback(void *ptr, int16_t *data, size_t len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read audio register at given address "addr".
|
* Read audio register at given address "addr".
|
||||||
|
|||||||
+36
-8
@@ -13,11 +13,11 @@
|
|||||||
* PERFORMANCE OF THIS SOFTWARE.
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Peanut-GB emulator settings
|
||||||
#define ENABLE_LCD 1
|
#define ENABLE_LCD 1
|
||||||
|
#define ENABLE_SOUND 1
|
||||||
#ifndef ENABLE_SOUND
|
#define PEANUT_GB_HIGH_LCD_ACCURACY 1
|
||||||
# define ENABLE_SOUND 0
|
#define PEANUT_GB_USE_BIOS 0
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Use DMA for all drawing to LCD. Benefits aren't fully realised at the moment
|
/* Use DMA for all drawing to LCD. Benefits aren't fully realised at the moment
|
||||||
* due to busy loops waiting for DMA completion. */
|
* due to busy loops waiting for DMA completion. */
|
||||||
@@ -40,6 +40,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/* RP2040 Headers */
|
/* RP2040 Headers */
|
||||||
|
#include <hardware/pio.h>
|
||||||
#include <hardware/clocks.h>
|
#include <hardware/clocks.h>
|
||||||
#include <hardware/dma.h>
|
#include <hardware/dma.h>
|
||||||
#include <hardware/spi.h>
|
#include <hardware/spi.h>
|
||||||
@@ -55,8 +56,10 @@
|
|||||||
|
|
||||||
/* Project headers */
|
/* Project headers */
|
||||||
#include "hedley.h"
|
#include "hedley.h"
|
||||||
|
#include "minigb_apu.h"
|
||||||
#include "peanut_gb.h"
|
#include "peanut_gb.h"
|
||||||
#include "mk_ili9225.h"
|
#include "mk_ili9225.h"
|
||||||
|
#include "i2s.h"
|
||||||
|
|
||||||
/* GPIO Connections. */
|
/* GPIO Connections. */
|
||||||
#define GPIO_UP 2
|
#define GPIO_UP 2
|
||||||
@@ -75,6 +78,15 @@
|
|||||||
#define GPIO_RST 21
|
#define GPIO_RST 21
|
||||||
#define GPIO_LED 22
|
#define GPIO_LED 22
|
||||||
|
|
||||||
|
#if ENABLE_SOUND
|
||||||
|
// Global variables for audio task
|
||||||
|
// stream contains N=AUDIO_SAMPLES samples
|
||||||
|
// each sample is 32 bits
|
||||||
|
// (16 bits for the left channel + 16 bits for the right channel in stereo interleaved format)
|
||||||
|
// This is intended to be played at AUDIO_SAMPLE_RATE Hz
|
||||||
|
uint16_t *stream;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* DMA channel for LCD communication. */
|
/* DMA channel for LCD communication. */
|
||||||
static uint dma_lcd;
|
static uint dma_lcd;
|
||||||
/* Definition of ROM data variable. Must be declared like:
|
/* Definition of ROM data variable. Must be declared like:
|
||||||
@@ -206,6 +218,7 @@ void core1_irq_dma_lcd_end_transfer(void)
|
|||||||
dma_channel_acknowledge_irq0(dma_lcd);
|
dma_channel_acknowledge_irq0(dma_lcd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_LCD
|
||||||
void core1_lcd_draw_line(const uint_fast8_t line)
|
void core1_lcd_draw_line(const uint_fast8_t line)
|
||||||
{
|
{
|
||||||
const uint16_t palette[3][4] = {
|
const uint16_t palette[3][4] = {
|
||||||
@@ -317,6 +330,7 @@ void main_core1(void)
|
|||||||
|
|
||||||
HEDLEY_UNREACHABLE();
|
HEDLEY_UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if ENABLE_LCD
|
#if ENABLE_LCD
|
||||||
void lcd_draw_line(struct gb_s *gb, const uint8_t pixels[LCD_WIDTH],
|
void lcd_draw_line(struct gb_s *gb, const uint8_t pixels[LCD_WIDTH],
|
||||||
@@ -422,8 +436,10 @@ int main(void)
|
|||||||
spi_set_format(spi0, 16, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
|
spi_set_format(spi0, 16, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
|
||||||
|
|
||||||
/* Start Core1, which processes requests to the LCD. */
|
/* Start Core1, which processes requests to the LCD. */
|
||||||
|
#if ENABLE_LCD
|
||||||
putstdio("CORE1 ");
|
putstdio("CORE1 ");
|
||||||
multicore_launch_core1(main_core1);
|
multicore_launch_core1(main_core1);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Initialise GB context. */
|
/* Initialise GB context. */
|
||||||
memcpy(rom_bank0, rom, sizeof(rom_bank0));
|
memcpy(rom_bank0, rom, sizeof(rom_bank0));
|
||||||
@@ -469,7 +485,20 @@ int main(void)
|
|||||||
//gb.direct.interlace = 1;
|
//gb.direct.interlace = 1;
|
||||||
#endif
|
#endif
|
||||||
#if ENABLE_SOUND
|
#if ENABLE_SOUND
|
||||||
|
// Allocate memory for the stream buffer
|
||||||
|
stream=malloc(AUDIO_BUFFER_SIZE_BYTES);
|
||||||
|
assert(stream!=NULL);
|
||||||
|
memset(stream,0,AUDIO_BUFFER_SIZE_BYTES); // Zero out the stream buffer
|
||||||
|
|
||||||
|
// Initialize I2S sound driver
|
||||||
|
i2s_config_t i2s_config = i2s_get_default_config();
|
||||||
|
i2s_config.sample_freq=AUDIO_SAMPLE_RATE;
|
||||||
|
i2s_config.dma_trans_count =AUDIO_SAMPLES;
|
||||||
|
i2s_init(&i2s_config);
|
||||||
|
|
||||||
|
// Initialize audio emulation
|
||||||
audio_init();
|
audio_init();
|
||||||
|
|
||||||
putstdio("AUDIO ");
|
putstdio("AUDIO ");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -479,9 +508,6 @@ int main(void)
|
|||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
int input;
|
int input;
|
||||||
#if ENABLE_SOUND
|
|
||||||
static uint16_t stream[1098];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
gb.gb_frame = 0;
|
gb.gb_frame = 0;
|
||||||
|
|
||||||
@@ -492,7 +518,9 @@ int main(void)
|
|||||||
|
|
||||||
frames++;
|
frames++;
|
||||||
#if ENABLE_SOUND
|
#if ENABLE_SOUND
|
||||||
audio_callback(NULL, stream, 1098);
|
audio_callback(NULL, stream, AUDIO_BUFFER_SIZE_BYTES);
|
||||||
|
// i2s_write(&i2s_config,stream,AUDIO_SAMPLES);
|
||||||
|
i2s_dma_write(&i2s_config, stream);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Update buttons state */
|
/* Update buttons state */
|
||||||
|
|||||||
Reference in New Issue
Block a user