--- /dev/null
+/*---------------------------------------------------------------------------*\\r
+\r
+ FILE........: dac_ut.c\r
+ AUTHOR......: David Rowe\r
+ DATE CREATED: May 31 2013\r
+\r
+ Plays a 500 Hz sine wave sampled at 16 kHz out of PA5 on a Discovery board,\r
+ or the speaker output of the SM1000.\r
+\r
+\*---------------------------------------------------------------------------*/\r
+\r
+/*\r
+ Copyright (C) 2013 David Rowe\r
+\r
+ All rights reserved.\r
+\r
+ This program is free software; you can redistribute it and/or modify\r
+ it under the terms of the GNU Lesser General Public License version 2.1, as\r
+ published by the Free Software Foundation. This program is\r
+ distributed in the hope that it will be useful, but WITHOUT ANY\r
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public\r
+ License for more details.\r
+\r
+ You should have received a copy of the GNU Lesser General Public License\r
+ along with this program; if not, see <http://www.gnu.org/licenses/>.\r
+*/\r
+\r
+#include <assert.h>\r
+#include "stm32f4_dacduc.h"\r
+\r
+#define SINE_SAMPLES 60\r
+\r
+\r
+/* 32 sample sine wave which at Fs=16kHz will be 500Hz. Note samples\r
+ are 16 bit 2's complement, the DAC driver convertsto 12 bit\r
+ unsigned. */\r
+\r
+short aWave[] = {4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,\r
+ 4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,4095,0,};\r
+\r
+short aSine[] = {390, 3981, 1410, 841, 4080, 841, 1410, 3981, 390, 2040, 3691, 100, 2671, 3240, 0, 3240, 2671, 100, 3691, 2040, 390, 3981, 1410, 841, 4080, 841, 1410, 3981, 390, 2040, 3691, 100, 2671, 3240, 0, 3240, 2671, 100, 3691, 2040,390, 3981, 1410, 841, 4080, 841, 1410, 3981, 390, 2040, 3691, 100, 2671, 3240, 0, 3240, 2671, 100, 3691, 2040\r
+};\r
+\r
+int main(void) {\r
+\r
+ dac_open(4*DAC_DUC_BUF_SZ);\r
+ while (1) {\r
+\r
+ /* keep DAC FIFOs topped up */\r
+\r
+ dac1_write((short*)aWave, SINE_SAMPLES);\r
+ }\r
+ \r
+}\r
+\r
--- /dev/null
+/*---------------------------------------------------------------------------*\\r
+\r
+ FILE........: stm32f4_dac.c\r
+ AUTHOR......: David Rowe\r
+ DATE CREATED: 1 June 2013\r
+\r
+ DAC driver module for STM32F4. DAC1 if fixed at Fs of 2Mhz.\r
+\r
+\*---------------------------------------------------------------------------*/\r
+\r
+/*\r
+ Copyright (C) 2013 David Rowe\r
+\r
+ All rights reserved.\r
+\r
+ This program is free software; you can redistribute it and/or modify\r
+ it under the terms of the GNU Lesser General Public License version 2.1, as\r
+ published by the Free Software Foundation. This program is\r
+ distributed in the hope that it will be useful, but WITHOUT ANY\r
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public\r
+ License for more details.\r
+\r
+ You should have received a copy of the GNU Lesser General Public License\r
+ along with this program; if not, see <http://www.gnu.org/licenses/>.\r
+*/\r
+\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include "stm32f4xx.h"\r
+#include "codec2_fifo.h"\r
+#include "stm32f4_dacduc.h"\r
+#include "debugblinky.h"\r
+\r
+/* write to these registers for 12 bit left aligned data, as per data sheet \r
+ make sure 4 least sig bits set to 0 */\r
+\r
+#define DAC_DHR12R1_ADDRESS 0x40007408\r
+#define DAC_DHR12R2_ADDRESS 0x40007414\r
+\r
+#define DAC_MAX 4096 /* maximum amplitude */\r
+\r
+/* y=mx+c mapping of samples16 bit shorts to DAC samples. Table: 74\r
+ of data sheet indicates With DAC buffer on, DAC range is limited to\r
+ 0x0E0 to 0xF1C at VREF+ = 3.6 V, we have Vref=3.3V which is close.\r
+ */\r
+\r
+#define M ((3868.0-224.0)/65536.0)\r
+#define C 2047.0\r
+\r
+\r
+static struct FIFO *dac1_fifo;\r
+static struct FIFO *dac2_fifo;\r
+\r
+static unsigned short dac1_buf[DAC_DUC_BUF_SZ];\r
+static unsigned short dac2_buf[DAC_BUF_SZ];\r
+\r
+static void tim6_config(void);\r
+static void tim7_config(void);\r
+static void dac1_config(void);\r
+static void dac2_config(void);\r
+\r
+int dac_underflow;\r
+\r
+void dac_open(int fifo_size) {\r
+\r
+ memset(dac1_buf, 32768, sizeof(short)*DAC_DUC_BUF_SZ);\r
+ memset(dac2_buf, 32768, sizeof(short)*DAC_BUF_SZ);\r
+\r
+ /* Create fifos */\r
+\r
+ dac1_fifo = fifo_create(fifo_size);\r
+ dac2_fifo = fifo_create(fifo_size);\r
+ assert(dac1_fifo != NULL);\r
+ assert(dac2_fifo != NULL);\r
+\r
+ /* Turn on the clocks we need -----------------------------------------------*/\r
+\r
+ /* DMA1 clock enable */\r
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);\r
+ /* GPIOA clock enable (to be used with DAC) */\r
+ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); \r
+ /* DAC Periph clock enable */\r
+ RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);\r
+\r
+ /* GPIO Pin configuration DAC1->PA.4, DAC2->PA.5 configuration --------------*/\r
+\r
+ GPIO_InitTypeDef GPIO_InitStructure;\r
+ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;\r
+ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;\r
+ GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;\r
+ GPIO_Init(GPIOA, &GPIO_InitStructure);\r
+\r
+ /* Timer and DAC 1 & 2 Configuration ----------------------------------------*/\r
+ tim7_config();\r
+ tim6_config(); \r
+ dac1_config();\r
+ dac2_config();\r
+\r
+ init_debug_blinky();\r
+}\r
+\r
+\r
+/* Call these puppies to send samples to the DACs. For your\r
+ convenience they accept signed 16 bit samples. */\r
+\r
+int dac1_write(short buf[], int n) { \r
+ return fifo_write(dac1_fifo, buf, n);\r
+}\r
+\r
+int dac2_write(short buf[], int n) { \r
+ return fifo_write(dac2_fifo, buf, n);\r
+}\r
+\r
+static void tim6_config(void)\r
+{\r
+ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;\r
+\r
+ /* TIM6 Periph clock enable */\r
+ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);\r
+ \r
+ /* --------------------------------------------------------\r
+ \r
+ TIM6 input clock (TIM6CLK) is set to 2 * APB1 clock (PCLK1), since\r
+ APB1 prescaler is different from 1 (see system_stm32f4xx.c and Fig\r
+ 13 clock tree figure in DM0031020.pdf).\r
+\r
+ Sample rate Fs = 2*PCLK1/TIM_ClockDivision \r
+ = (HCLK/2)/TIM_ClockDivision\r
+ \r
+ ----------------------------------------------------------- */\r
+\r
+ /* Time base configuration */\r
+\r
+ TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); \r
+ TIM_TimeBaseStructure.TIM_Period = 80; \r
+ TIM_TimeBaseStructure.TIM_Prescaler = 0; \r
+ TIM_TimeBaseStructure.TIM_ClockDivision = 0; \r
+ TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; \r
+ TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);\r
+\r
+ /* TIM6 TRGO selection */\r
+\r
+ TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);\r
+ \r
+ /* TIM6 enable counter */\r
+\r
+ TIM_Cmd(TIM6, ENABLE);\r
+}\r
+\r
+/* Sets up tim7 to run at a high sample rate */\r
+void tim7_config(void)\r
+{\r
+ /* Set up tim7 */\r
+\r
+\r
+ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;\r
+\r
+ /* TIM7 Periph clock enable */\r
+ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);\r
+ \r
+ /* --------------------------------------------------------\r
+\r
+ \r
+ TIM7 input clock (TIM7CLK) is set to 2 * APB1 clock (PCLK1), since\r
+ APB1 prescaler is different from 1 (see system_stm32f4xx.c and Fig\r
+ 13 clock tree figure in DM0031020.pdf).\r
+\r
+ Sample rate Fs = 2*PCLK1/TIM_ClockDivision \r
+ = (HCLK/2)/TIM_ClockDivision\r
+ \r
+ ----------------------------------------------------------- */\r
+\r
+ /* Time base configuration */\r
+\r
+ TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); \r
+ TIM_TimeBaseStructure.TIM_Period = 41; \r
+ TIM_TimeBaseStructure.TIM_Prescaler = 0; \r
+ TIM_TimeBaseStructure.TIM_ClockDivision = 0; \r
+ TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; \r
+ TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure);\r
+\r
+ /* TIM7 TRGO selection */\r
+\r
+ TIM_SelectOutputTrigger(TIM7, TIM_TRGOSource_Update);\r
+ \r
+ /* TIM7 enable counter */\r
+\r
+ TIM_Cmd(TIM7, ENABLE);\r
+}\r
+\r
+static void dac1_config(void)\r
+{\r
+ DAC_InitTypeDef DAC_InitStructure;\r
+ DMA_InitTypeDef DMA_InitStructure;\r
+ NVIC_InitTypeDef NVIC_InitStructure;\r
+ \r
+ /* DAC channel 1 Configuration */\r
+\r
+ /* \r
+ This line fixed a bug that cost me 5 days, bad wave amplitude\r
+ value, and some STM32F4 periph library bugs caused triangle wave\r
+ geneartion to be enable resulting in a low level tone on the\r
+ SM1000, that we thought was caused by analog issues like layour\r
+ or power supply biasing\r
+ */\r
+ DAC_StructInit(&DAC_InitStructure); \r
+\r
+ DAC_InitStructure.DAC_Trigger = DAC_Trigger_T7_TRGO; \r
+ DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;\r
+ DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;\r
+ DAC_Init(DAC_Channel_1, &DAC_InitStructure);\r
+\r
+ /* DMA1_Stream5 channel7 configuration **************************************/\r
+ /* Table 35 page 219 of the monster data sheet */\r
+\r
+ DMA_DeInit(DMA1_Stream5);\r
+ DMA_InitStructure.DMA_Channel = DMA_Channel_7; \r
+ DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R1_ADDRESS;\r
+ DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dac1_buf;\r
+ DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;\r
+ DMA_InitStructure.DMA_BufferSize = DAC_DUC_BUF_SZ;\r
+ DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;\r
+ DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;\r
+ DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;\r
+ DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;\r
+ DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;\r
+ DMA_InitStructure.DMA_Priority = DMA_Priority_High;\r
+ DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; \r
+ DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;\r
+ DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;\r
+ DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;\r
+ DMA_Init(DMA1_Stream5, &DMA_InitStructure);\r
+\r
+ /* Enable DMA Half & Complete interrupts */\r
+\r
+ DMA_ITConfig(DMA1_Stream5, DMA_IT_TC | DMA_IT_HT, ENABLE);\r
+\r
+ /* Enable the DMA Stream IRQ Channel */\r
+\r
+ NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream5_IRQn;\r
+ NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;\r
+ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;\r
+ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;\r
+ NVIC_Init(&NVIC_InitStructure); \r
+\r
+ /* Enable DMA1_Stream5 */\r
+\r
+ DMA_Cmd(DMA1_Stream5, ENABLE);\r
+\r
+ /* Enable DAC Channel 1 */\r
+\r
+ DAC_Cmd(DAC_Channel_1, ENABLE);\r
+\r
+ /* Enable DMA for DAC Channel 1 */\r
+\r
+ DAC_DMACmd(DAC_Channel_1, ENABLE);\r
+}\r
+\r
+static void dac2_config(void)\r
+{\r
+ DAC_InitTypeDef DAC_InitStructure;\r
+ DMA_InitTypeDef DMA_InitStructure;\r
+ NVIC_InitTypeDef NVIC_InitStructure;\r
+ \r
+ /* DAC channel 2 Configuration (see notes in dac1_config() above) */\r
+\r
+ DAC_StructInit(&DAC_InitStructure);\r
+ DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;\r
+ DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;\r
+ DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;\r
+ DAC_Init(DAC_Channel_2, &DAC_InitStructure);\r
+\r
+ /* DMA1_Stream6 channel7 configuration **************************************/\r
+\r
+ DMA_DeInit(DMA1_Stream6);\r
+ DMA_InitStructure.DMA_Channel = DMA_Channel_7; \r
+ DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R2_ADDRESS;\r
+ DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dac2_buf;\r
+ DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;\r
+ DMA_InitStructure.DMA_BufferSize = DAC_BUF_SZ;\r
+ DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;\r
+ DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;\r
+ DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;\r
+ DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;\r
+ DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;\r
+ DMA_InitStructure.DMA_Priority = DMA_Priority_High;\r
+ DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; \r
+ DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;\r
+ DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;\r
+ DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;\r
+ DMA_Init(DMA1_Stream6, &DMA_InitStructure);\r
+\r
+ /* Enable DMA Half & Complete interrupts */\r
+\r
+ DMA_ITConfig(DMA1_Stream6, DMA_IT_TC | DMA_IT_HT, ENABLE);\r
+\r
+ /* Enable the DMA Stream IRQ Channel */\r
+\r
+ NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream6_IRQn;\r
+ NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;\r
+ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;\r
+ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;\r
+ NVIC_Init(&NVIC_InitStructure); \r
+\r
+ /* Enable DMA1_Stream6 */\r
+\r
+ DMA_Cmd(DMA1_Stream6, ENABLE);\r
+\r
+ /* Enable DAC Channel 2 */\r
+\r
+ DAC_Cmd(DAC_Channel_2, ENABLE);\r
+\r
+ /* Enable DMA for DAC Channel 2 */\r
+\r
+ DAC_DMACmd(DAC_Channel_2, ENABLE);\r
+\r
+}\r
+\r
+/******************************************************************************/\r
+/* STM32F4xx Peripherals Interrupt Handlers */\r
+/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */\r
+/* available peripheral interrupt handler's name please refer to the startup */\r
+/* file (startup_stm32f40xx.s/startup_stm32f427x.s). */\r
+/******************************************************************************/\r
+\r
+/*\r
+ This function handles DMA1 Stream 5 interrupt request for DAC1.\r
+*/\r
+\r
+void DMA1_Stream5_IRQHandler(void) {\r
+ GPIOE->ODR |= (1 << 1);\r
+\r
+ /* Transfer half empty interrupt - refill first half */\r
+\r
+ if(DMA_GetITStatus(DMA1_Stream5, DMA_IT_HTIF5) != RESET) {\r
+ /* fill first half from fifo */\r
+ \r
+ if (fifo_read(dac1_fifo, (short*)dac1_buf, DAC_DUC_BUF_SZ/2) == -1) {\r
+ memset(dac1_buf, 0, sizeof(short)*DAC_DUC_BUF_SZ/2);\r
+ dac_underflow++;\r
+ }\r
+\r
+ /* Clear DMA Stream Transfer Complete interrupt pending bit */\r
+\r
+ DMA_ClearITPendingBit(DMA1_Stream5, DMA_IT_HTIF5); \r
+ }\r
+\r
+ /* Transfer complete interrupt - refill 2nd half */\r
+\r
+ if(DMA_GetITStatus(DMA1_Stream5, DMA_IT_TCIF5) != RESET) {\r
+ /* fill second half from fifo */\r
+\r
+ if (fifo_read(dac1_fifo, (short*)(dac1_buf+DAC_DUC_BUF_SZ/2), DAC_DUC_BUF_SZ/2) == -1) {\r
+ memset(dac1_buf, 0, sizeof(short)*DAC_DUC_BUF_SZ/2);\r
+ dac_underflow++;\r
+ } /* Clear DMA Stream Transfer Complete interrupt pending bit */\r
+\r
+ DMA_ClearITPendingBit(DMA1_Stream5, DMA_IT_TCIF5); \r
+ }\r
+\r
+ GPIOE->ODR &= ~(1 << 1);\r
+}\r
+\r
+/*\r
+ This function handles DMA1 Stream 6 interrupt request for DAC2.\r
+*/\r
+\r
+void DMA1_Stream6_IRQHandler(void) {\r
+ int i, j, sam;\r
+ short signed_buf[DAC_BUF_SZ/2];\r
+\r
+ GPIOE->ODR |= (1 << 2);\r
+\r
+ /* Transfer half empty interrupt - refill first half */\r
+\r
+ if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_HTIF6) != RESET) {\r
+ /* fill first half from fifo */\r
+\r
+ if (fifo_read(dac2_fifo, signed_buf, DAC_BUF_SZ/2) == -1) {\r
+ memset(signed_buf, 0, sizeof(short)*DAC_BUF_SZ/2);\r
+ dac_underflow++;\r
+ }\r
+\r
+ /* convert to unsigned */\r
+\r
+ for(i=0; i<DAC_BUF_SZ/2; i++) {\r
+ sam = (int)(M*(float)signed_buf[i] + C);\r
+ dac2_buf[i] = (unsigned short)sam;\r
+ }\r
+\r
+ /* Clear DMA Stream Transfer Complete interrupt pending bit */\r
+\r
+ DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_HTIF6); \r
+ }\r
+\r
+ /* Transfer complete interrupt - refill 2nd half */\r
+\r
+ if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_TCIF6) != RESET) {\r
+ /* fill second half from fifo */\r
+\r
+ if (fifo_read(dac2_fifo, signed_buf, DAC_BUF_SZ/2) == -1) {\r
+ memset(signed_buf, 0, sizeof(short)*DAC_BUF_SZ/2);\r
+ dac_underflow++;\r
+ }\r
+\r
+ /* convert to unsigned */\r
+\r
+ for(i=0, j=DAC_BUF_SZ/2; i<DAC_BUF_SZ/2; i++,j++) {\r
+ sam = (int)(M*(float)signed_buf[i] + C);\r
+ dac2_buf[j] = (unsigned short)sam;\r
+ }\r
+\r
+ /* Clear DMA Stream Transfer Complete interrupt pending bit */\r
+\r
+ DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_TCIF6); \r
+ }\r
+\r
+ GPIOE->ODR &= ~(1 << 2);\r
+}\r
+\r