Arduino_STM32 - joric/ts80player GitHub Wiki
Uses unofficial STM32 arduino platform by Roger Clark. Relatively easy to set up and the best sound quality yet.
Setup
Copy Arduino_STM32 repository to %USERPROFILE%\Documents\Arduino\hardware\Arduino_STM32-master
, restart IDE.
Miniware irons don't have XTAL, and there's no weak SystemClock_Config
handler as in STM32duino, so I used a modified Nucleo_F103rb configuration (this board doesn't have external crystal as well).
Arduino IDE settings for TS80 (also works for Bluepill with TS80 bootloader):
- Board: "STM Nucleo F103RB (STLink)"
- Variant: "Nucleo F103 at 64 MHz"
Bootloader settings
You have to modify ROM offset to 0x4000 and use hex instead of bin:
Edit STM32F1/boards txt
:
nucleo_f103rb.upload.tool=maple_upload
nucleo_f103rb.upload.file_type=hex
nucleo_f103rb.build.vect=VECT_TAB_ADDR=0x8004000
Edit STM32F1/platform.txt
:
compiler.elf2hex.flags=-O ihex
recipe.objcopy.hex.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.hex"
recipe.output.tmp_file={build.project_name}.hex
recipe.output.save_file={build.project_name}.{build.variant}.hex
tools.maple_upload.upload.pattern="{path}/{cmd}" {serial.port.file} {upload.altID} {upload.usbID} "{build.path}/{build.project_name}.hex"
Edit STM32F1/variants/nucleo_f103rb/ld/mem-jtag.in
:
rom (rx) : ORIGIN = 0x08004000, LENGTH = 128K
Edit tools/win/maple_upload.bat
:
set tmpBinFilePath=%4
set tmpBinFilePath=%tmpBinFilePath:/=\%
echo COPY %tmpBinFilePath% E:\
copy %tmpBinFilePath% E:\
Firmware
- https://github.com/joric/ts80player/tree/master/firmware/Arduino_STM32 (this repository)
- https://github.com/ChrisMicro/BluePillSound (audio examples I used)
It's possible to remap sound pin from B0 to A6 (TS80 tip pin):
Edit stm32f103.h
:
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE )
Edit main.ino
:
void startDMA()
{
RCC->AHBENR |= RCC_AHBENR_DMA1EN; // enable DMA
// channel 1: mem:8bit -> peri:8bit
DMA1_Channel1->CNDTR = DMABUFFERLENGTH;
DMA1_Channel1->CMAR = (uint32_t)DmaBuffer;
DMA1_Channel1->CPAR = (uint32_t) & (GPIOA->ODR);
DMA1_Channel1->CCR = 0;
DMA1_Channel1->CCR = DMA_CCR1_MEM2MEM | DMA_CCR1_PL | DMA_CCR1_MINC | DMA_CCR1_CIRC | DMA_CCR1_DIR | DMA_CCR1_EN;
}
#define PIN_PA0 6 // speaker pin, you can choose 0..7, don't forget
// to adjust the pin initializaion in setup when you change this value
#define PORTA_PINMASK (1<<PIN_PA0)
#define MAXVALUE 255
// Sigma Delta DAC
void writeDac( uint32_t sollwert )
{
int32_t integrator = 0;
static uint32_t oldValue = 0;
uint32_t n;
// sigma delta DAC, hold the DAC value for n-steps constant
for (n = 0; n < DMABUFFERLENGTH; n++)
{
integrator += sollwert - oldValue;
if (integrator > 0)
{
oldValue = MAXVALUE;
DmaBuffer[n]=PORTA_PINMASK;
}
else
{
oldValue = 0;
DmaBuffer[n]=0;
}
}
}
The only good result I could get is with _080_dmaSigmaDelta sample, the "fast" and "buffered" dma versions had terrible garbled sound. It heavily depends on hardware, try DMABUFFERLENGTH 32
for the 44100 sample rate when writing directly to dma as writeDac(amp)
. Samples should be converted to 8-bit mono.
Issues
writeDac examples turn off OLED, because they apparently write 0 (GND) to A15 (OLED_RST), need some kind of a mask:
-DmaBuffer[n] = 0;
+DmaBuffer[n] = 0xff;
-DmaBuffer[n] = PORTA_PINMASK;
+DmaBuffer[n] = 0xff ^ PORTA_PINMASK; // this doesn't reset OLED
The mask 0xff somehow fixes that (maybe A15 is shorted to lower pins). Sigma-delta DMA examples apparently write all 16 pins in a bank, so it needs to be dealt with as well.
USB Mass storage
There's no external crystal (HSE) on this board, only the internal one (HSI). For F103 the 8Mhz HSI is divided by 2 before it goes to the PLL, the PLL can then multiply that by 12 to create 48MHz and both SYSCLK and USB will then be 48MHz. Unfortunately, the USB Prescaler 1.5 cannot be used, as the PLL cannot multiply by 18 so we can't have both 72MHz SYSCLK and 48MHz USB. The only working configuration is f_cpu=48000000L, RCC_PLLMUL_12 and RCC_USB_SYSCLK_DIV_1.
Edit STM32F1/boards txt
:
nucleo_f103rb.menu.device_variant.NucleoF103_HSI=Nucleo F103 @ 48 MHz
nucleo_f103rb.menu.device_variant.NucleoF103_HSI.build.f_cpu=48000000L
Edit STM32F1/variants/nucleo_f103rb/wirish/boards_setup.cpp
:
#define BOARD_RCC_PLLMUL RCC_PLLMUL_12
rcc_set_prescaler(RCC_PRESCALER_USB, RCC_USB_SYSCLK_DIV_1);
Taken from STM32F1/libraries/USBComposite/examples/mass
:
#include <USBMassStorage.h>
USBMassStorage MassStorage;
#include "image.h"
bool write(const uint8_t *writebuff, uint32_t memoryOffset, uint16_t transferLength) {
memcpy(image+SCSI_BLOCK_SIZE*memoryOffset, writebuff, SCSI_BLOCK_SIZE*transferLength);
return true;
}
bool read(uint8_t *readbuff, uint32_t memoryOffset, uint16_t transferLength) {
memcpy(readbuff, image+SCSI_BLOCK_SIZE*memoryOffset, SCSI_BLOCK_SIZE*transferLength);
return true;
}
void setup() {
MassStorage.setDriveData(0, sizeof(image)/SCSI_BLOCK_SIZE, read, write);
MassStorage.registerComponent();
MassStorage.begin();
}
void loop() {
MassStorage.loop();
}
There's more to do, e.g writing to flash because it's not persistent and there's almost no memory left but it works.