-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
11fd729
commit 7a021a2
Showing
2 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
218 changes: 218 additions & 0 deletions
218
_083_dmaSigmaDeltaBufferedSound/_083_dmaSigmaDeltaBufferedSound.ino
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
****************************************************************************** | ||
*/ | ||
|
||
|