Skip to content

Commit

Permalink
buffered sound player added
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisMicro committed Apr 18, 2017
1 parent 11fd729 commit 7a021a2
Show file tree
Hide file tree
Showing 2 changed files with 348 additions and 0 deletions.
218 changes: 218 additions & 0 deletions _083_dmaSigmaDeltaBufferedSound/_083_dmaSigmaDeltaBufferedSound.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/*
Sigma Delta DAC example using DMA and double buffering
Generate a sine wave, fill a buffer and play the samples in a timer driven interrupt routine.
Hardware: Poti on analog input 0
Library dependency: you need the libmaple hardware timer
http://docs.leaflabs.com/docs.leaflabs.com/index.html
This is just an experiment using a buffer filled with the sigmal delta principle
and output it by the DMA to port PB0.
The DMA is programmed bare metal without any wrapper functions.
https://github.com/ChrisMicro/BluePillSound
************************************************************************
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
********************* list of outhors **********************************
v0.1 4.4.2017 C. -H-A-B-E-R-E-R- initial version
v0.2 5.4.2017 C. -H-A-B-E-R-E-R- higher sampling rate due to 32bit table
v0.3 6.4.2017 C. -H-A-B-E-R-E-R- interrupt driven sine wave oscillator
v0.3 18.4.2017 C. -H-A-B-E-R-E-R- buffered player
It is mandatory to keep the list of authors in this code.
Please add your name if you improve/extend something
*/

#include "stm32f103.h"

#define DACSAMPLINGRATE_HZ 10000
#define TIMER_INTTERUPT_US 1000000UL / DACSAMPLINGRATE_HZ

HardwareTimer timer(2);

#define DMABUFFERLENGTH 100

volatile uint32_t DmaBuffer[DMABUFFERLENGTH];

// good DMA article:
// https://vjordan.info/log/fpga/stm32-bare-metal-start-up-and-real-bit-banging-speed.html
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) & (GPIOB->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;
}

// maple timer
// http://docs.leaflabs.com/docs.leaflabs.com/index.html
void startTimer()
{
// Pause the timer while we're configuring it
timer.pause();

// Set up period
timer.setPeriod(TIMER_INTTERUPT_US); // in microseconds

// Set up an interrupt on channel 1
timer.setChannel1Mode(TIMER_OUTPUT_COMPARE);
timer.setCompare(TIMER_CH1, 1); // Interrupt 1 count after each update
timer.attachCompare1Interrupt(DACsampleRateHandler);

// Refresh the timer's count, prescale, and overflow
timer.refresh();

// Start the timer counting
timer.resume();
}

uint16_t SineTable[256];

#define SPEAKERPIN PB0 // speaker pin, be sure that it corresponds to the DMA pin setup in the next line
#define PIN_PB 0 // speaker pin, you can choose 0..7, 0 means PB0, 1 means PB1 ...
#define PORTB_PINMASK (1<<PIN_PB)

uint32_t CoarseSigmaDeltaTable[] = {0x00000000 * PORTB_PINMASK, 0x01000000 * PORTB_PINMASK, 0x01000100 * PORTB_PINMASK, 0x01010100 * PORTB_PINMASK, 0x01010101 * PORTB_PINMASK};

// Sigma Delta DAC
// input value 10 bit 0..1023
void writeDac( uint32_t sollwert )
{
uint32_t integrator = 0;
static uint32_t oldValue = 0;

uint32_t n;

// sigma delta DAC
for (n = 0; n < DMABUFFERLENGTH / 4; n++)
{
integrator += sollwert - oldValue;
oldValue = integrator & 0b11100000000;
DmaBuffer[n] = CoarseSigmaDeltaTable[ oldValue >> 8 ];
}
}

typedef uint16_t buf_t;
#define SOUNDBUFFERLENGTH 256
volatile buf_t SoundBuffer0[SOUNDBUFFERLENGTH];
volatile buf_t SoundBuffer1[SOUNDBUFFERLENGTH];

volatile buf_t * volatile SoundBufferCurrentlyPlaying;

void DACsampleRateHandler()
{
static uint32_t bufferIndex=0;

writeDac(SoundBufferCurrentlyPlaying[bufferIndex++]);

if( bufferIndex >= SOUNDBUFFERLENGTH )
{
bufferIndex=0;
if( SoundBufferCurrentlyPlaying != SoundBuffer1 )SoundBufferCurrentlyPlaying = SoundBuffer1;
else SoundBufferCurrentlyPlaying = SoundBuffer0;
}

}
#define LEDPIN PC13 // Blue Pill LED
#define INITLED pinMode(LEDPIN, OUTPUT)

#define LEDON digitalWrite(LEDPIN, HIGH)
#define LEDOFF digitalWrite(LEDPIN, LOW)

void toggleLed()
{
static uint8_t flag=0;

if(flag) LEDON;
else LEDOFF;

flag^=1;
}

volatile buf_t * volatile SoundBuffer;

void setup()
{
uint32_t n;

pinMode(PC13, OUTPUT);
pinMode(SPEAKERPIN, OUTPUT);
Serial.begin(115200);
Serial.println("SPI SIGMA DELTA SOUND calculation performance");

delay(100);
SoundBufferCurrentlyPlaying = SoundBuffer0;
SoundBuffer = SoundBuffer1;
startDMA();

// ** just check the write speed **
uint32_t startTime = micros();
for (n = 0; n < 1000; n++) writeDac(0); // dummy write silence
uint32_t stopTime = micros();

uint32_t DacSamplingFrequency;
Serial.print("DAC write time for 1000 samples in us: "); Serial.println(stopTime - startTime);
DacSamplingFrequency = 1000000UL * 1000 / (stopTime - startTime);
Serial.print("maximum possible DAC sampling frequency [Hz]: "); Serial.println(DacSamplingFrequency);

// *********************************

for (n = 0; n < 256; n++) SineTable[n] = (sin(2 * PI * n / 255) + 1) * 500;

startTimer();
}

volatile uint32_t Oscillator1_Frequency_Hz=440;

void updateSound()
{
uint32_t amplitude=500;
/*
// fast integer sine wave
static uint16_t Phase;
for(uint32_t n=0;n<SOUNDBUFFERLENGTH;n++)
{
Phase += 256 * 256 * Oscillator1_Frequency_Hz / DACSAMPLINGRATE_HZ;
SoundBuffer[n]=(SineTable[Phase >> 8]);
}*/
// float sine wave calculation is very slow on STM32f103@72MHz for real time calculation
// 16ms/256 samples @ fab=10kHz
// ~63us per sample ( sigma delta dac with spi DMA running in parallel )
static float phase=0;
float deltaPhase;
deltaPhase=2*PI*Oscillator1_Frequency_Hz / DACSAMPLINGRATE_HZ;

for(uint32_t n=0;n<SOUNDBUFFERLENGTH;n++)
{
phase+=deltaPhase;
if( phase > 2 *PI ) phase -= 2 * PI;
SoundBuffer[n]=(sin(phase)+1)*amplitude;
}

}

void loop()
{
SoundBuffer = SoundBufferCurrentlyPlaying;
while(SoundBuffer == SoundBufferCurrentlyPlaying); // wait until buffer is emtpy

LEDON; // debug led for processing time measuremt with oscilloscope
updateSound();
LEDOFF;

}
130 changes: 130 additions & 0 deletions _083_dmaSigmaDeltaBufferedSound/stm32f103.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* STM32F103 register definition
*
* Today it is always a problem to compile a project due to missing dependencies.
*
* For this reason this file contains only a the necessary register definitions
* without any dependencies so you can easily compile your project.
*
*/
#define __IO volatile

typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;

typedef struct
{
__IO uint32_t CCR;
__IO uint32_t CNDTR;
__IO uint32_t CPAR;
__IO uint32_t CMAR;

} DMA_Channel_TypeDef;

typedef struct
{
__IO uint32_t ISR;
__IO uint32_t IFCR;

} DMA_TypeDef;

typedef struct
{
__IO uint32_t CR;
__IO uint32_t CFGR;
__IO uint32_t CIR;
__IO uint32_t APB2RSTR;
__IO uint32_t APB1RSTR;
__IO uint32_t AHBENR;
__IO uint32_t APB2ENR;
__IO uint32_t APB1ENR;
__IO uint32_t BDCR;
__IO uint32_t CSR;

#ifdef STM32F10X_CL
__IO uint32_t AHBRSTR;
__IO uint32_t CFGR2;
#endif /* STM32F10X_CL */

#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)
uint32_t RESERVED0;
__IO uint32_t CFGR2;
#endif /* STM32F10X_LD_VL || STM32F10X_MD_VL || STM32F10X_HD_VL */
} RCC_TypeDef;

#define FLASH_BASE ((uint32_t)0x08000000) /*!< FLASH base address in the alias region */
#define SRAM_BASE ((uint32_t)0x20000000) /*!< SRAM base address in the alias region */
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */

#define SRAM_BB_BASE ((uint32_t)0x22000000) /*!< SRAM base address in the bit-band region */
#define PERIPH_BB_BASE ((uint32_t)0x42000000) /*!< Peripheral base address in the bit-band region */

#define FSMC_R_BASE ((uint32_t)0xA0000000) /*!< FSMC registers base address */

/*!< Peripheral memory map */
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
#define DMA1_BASE (AHBPERIPH_BASE + 0x0000)
#define DMA1_Channel1_BASE (AHBPERIPH_BASE + 0x0008)
#define DMA1_Channel1 ((DMA_Channel_TypeDef *) DMA1_Channel1_BASE)

/******************* Bit definition for DMA_CCR1 register *******************/
#define DMA_CCR1_EN ((uint16_t)0x0001) /*!< Channel enable*/
#define DMA_CCR1_TCIE ((uint16_t)0x0002) /*!< Transfer complete interrupt enable */
#define DMA_CCR1_HTIE ((uint16_t)0x0004) /*!< Half Transfer interrupt enable */
#define DMA_CCR1_TEIE ((uint16_t)0x0008) /*!< Transfer error interrupt enable */
#define DMA_CCR1_DIR ((uint16_t)0x0010) /*!< Data transfer direction */
#define DMA_CCR1_CIRC ((uint16_t)0x0020) /*!< Circular mode */
#define DMA_CCR1_PINC ((uint16_t)0x0040) /*!< Peripheral increment mode */
#define DMA_CCR1_MINC ((uint16_t)0x0080) /*!< Memory increment mode */

#define DMA_CCR1_PSIZE ((uint16_t)0x0300) /*!< PSIZE[1:0] bits (Peripheral size) */
#define DMA_CCR1_PSIZE_0 ((uint16_t)0x0100) /*!< Bit 0 */
#define DMA_CCR1_PSIZE_1 ((uint16_t)0x0200) /*!< Bit 1 */

#define DMA_CCR1_MSIZE ((uint16_t)0x0C00) /*!< MSIZE[1:0] bits (Memory size) */
#define DMA_CCR1_MSIZE_0 ((uint16_t)0x0400) /*!< Bit 0 */
#define DMA_CCR1_MSIZE_1 ((uint16_t)0x0800) /*!< Bit 1 */

#define DMA_CCR1_PL ((uint16_t)0x3000) /*!< PL[1:0] bits(Channel Priority level) */
#define DMA_CCR1_PL_0 ((uint16_t)0x1000) /*!< Bit 0 */
#define DMA_CCR1_PL_1 ((uint16_t)0x2000) /*!< Bit 1 */

#define DMA_CCR1_MEM2MEM ((uint16_t)0x4000) /*!< Memory to memory mode */

#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE )
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE )

#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
#define RCC ((RCC_TypeDef *) RCC_BASE)

/**
******************************************************************************
Released into the public domain.
This work is free: you can redistribute it and/or modify it under the terms of
Creative Commons Zero license v1.0
This work is licensed under the Creative Commons Zero 1.0 United States License.
To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0/
or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco,
California, 94105, USA.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.
******************************************************************************
*/


0 comments on commit 7a021a2

Please sign in to comment.