#include "iir_tuner.h"
struct FIFO *adc1_fifo;
-unsigned short adc_buf[ADC_TUNER_BUF_SZ];
+unsigned short adc_buf[ADC_TUNER_BUF_SZ], *padc_buf;
int adc_overflow1;
int half,full;
static short tuner_en = 1;
static void tim2_config(void);
+//#define DUMMY_SIGNAL
+#ifdef DUMMY_SIGNAL
+unsigned short sine[ADC_TUNER_BUF_SZ];
+#endif
void adc_open(int fifo_sz) {
adc1_fifo = fifo_create(fifo_sz);
ADC_Cmd(ADC1,ENABLE);
ADC_SoftwareStartConv(ADC1);
+
+ padc_buf = adc_buf;
+
+ #ifdef DUMMY_SIGNAL
+ int i;
+
+ /* Fs/4 sine wave, right in the middle of the pass band ! */
+
+ for(i=0; i<ADC_TUNER_BUF_SZ; i++)
+ sine[i] = 32767;
+ for(i=1; i<ADC_TUNER_BUF_SZ; i+=4)
+ sine[i] += 32767/4;
+ for(i=3; i<ADC_TUNER_BUF_SZ; i+=4)
+ sine[i] -= 32767/4;
+ padc_buf = sine;
+ #endif
+
}
void DMA2_Stream0_IRQHandler(void) {
float dec_buf[ADC_TUNER_N/2];
- int i;
/* PE0 is asserted high for the duration of this ISR */
GPIOE->ODR |= (1 << 0);
- //#define DUMMY_SIGNAL
- #ifdef DUMMY_SIGNAL
-
- /* Fs/4 sine wave, right in the middle of the pass band ! */
-
- for(i=0; i<ADC_TUNER_BUF_SZ; i++)
- adc_buf[i] = 32767;
- for(i=1; i<ADC_TUNER_BUF_SZ; i+=4)
- adc_buf[i] += 32767;
- for(i=3; i<ADC_TUNER_BUF_SZ; i+=4)
- adc_buf[i] -= 32767;
-
- #endif
-
/* Half transfer interrupt */
if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_HTIF0) != RESET) {
half++;
if (tuner_en) {
- iir_tuner(dec_buf, adc_buf);
+ iir_tuner(dec_buf, padc_buf);
- /* write first half to fifo */
+ /* write first half to fifo. Note we are writing ADC_TUNER_N/2 floats,
+ which is equivalent to ADC_TUNER_N shorts. */
- if (fifo_write(adc1_fifo, (short*)dec_buf, ADC_TUNER_N) == -1) {
+ if (fifo_write(adc1_fifo, (short*)dec_buf, ADC_TUNER_N) == -1) {
adc_overflow1++;
}
}
- else
- fifo_write(adc1_fifo, (short*)adc_buf, ADC_TUNER_BUF_SZ/2);
+ else // note: we dump signed shorts when tuner off
+ fifo_write(adc1_fifo, (short*)padc_buf, ADC_TUNER_BUF_SZ/2);
/* Clear DMA Stream Transfer Complete interrupt pending bit */
full++;
if (tuner_en) {
- iir_tuner(dec_buf, &adc_buf[ADC_TUNER_BUF_SZ/2]);
+ iir_tuner(dec_buf, &padc_buf[ADC_TUNER_BUF_SZ/2]);
/* write second half to fifo */
}
}
else
- fifo_write(adc1_fifo, (short*)&adc_buf[ADC_TUNER_BUF_SZ/2], ADC_TUNER_BUF_SZ/2);
+ fifo_write(adc1_fifo, (short*)&padc_buf[ADC_TUNER_BUF_SZ/2], ADC_TUNER_BUF_SZ/2);
/* Clear DMA Stream Transfer Complete interrupt pending bit */
--- /dev/null
+/*---------------------------------------------------------------------------*\\r
+\r
+ FILE........: stm32f4_dacloduc.c\r
+ AUTHOR......: David Rowe\r
+ DATE CREATED: Sep 2015\r
+\r
+ Experimental DAC driver module for STM32F4 that includes a low IF\r
+ Digital Up Converter (DUC). The Fs=96kHz signal is mixed up by a\r
+ (real) 24 kHz (Fs/4) local oscillator, then output by DAC1.\r
+\r
+ DAC1 is connected to pin PA4.\r
+\r
+\*---------------------------------------------------------------------------*/\r
+\r
+/*\r
+ Copyright (C) 2015 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 <math.h>\r
+#include "stm32f4xx.h"\r
+#include "codec2_fifo.h"\r
+#include "stm32f4_dac.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
+\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
+static struct FIFO *dac1_fifo;\r
+\r
+static unsigned short dac1_buf[DAC_BUF_SZ];\r
+\r
+static void tim6_config(void);\r
+static void dac1_config(void);\r
+\r
+int dac_underflow;\r
+\r
+short signed_buf[DAC_BUF_SZ/2];\r
+\r
+#define MAX_AMP 32767\r
+\r
+void dac_open(int fifo_size) {\r
+ memset(dac1_buf, 32768, sizeof(short)*DAC_BUF_SZ);\r
+\r
+ /* Create fifo */\r
+\r
+ dac1_fifo = fifo_create(fifo_size);\r
+ assert(dac1_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 configuration --------------------------*/\r
+\r
+ GPIO_InitTypeDef GPIO_InitStructure;\r
+ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;\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 Configuration --------------------------------------------*/\r
+\r
+ tim6_config(); \r
+ dac1_config();\r
+\r
+ init_debug_blinky();\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
+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
+ = 84E6/TIM_ClockDivision (usually)\r
+\r
+ ----------------------------------------------------------- */\r
+\r
+ /* Time base configuration */\r
+\r
+ TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); \r
+ TIM_TimeBaseStructure.TIM_Period = 875-1; /* 96 kHz */\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
+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
+ generation to be enable resulting in a low level tone on the\r
+ SM1000, that we thought was caused by analog issues like layout\r
+ or power supply biasing\r
+ */\r
+ DAC_StructInit(&DAC_InitStructure); \r
+\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_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_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
+\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
+ int i, j, sam;\r
+ short signed_buf[DAC_BUF_SZ/2];\r
+\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
+\r
+ /* fill first half from fifo */\r
+\r
+ if (fifo_read(dac1_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
+ for(i=0; i<DAC_BUF_SZ/2; i++) {\r
+ sam = (int)(M*(float)signed_buf[i] + C);\r
+ dac1_buf[i] = (unsigned short)sam;\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
+\r
+ /* fill second half from fifo */\r
+\r
+ if (fifo_read(dac1_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
+ 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
+ dac1_buf[j] = (unsigned short)sam;\r
+ }\r
+\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
Remarkably, it compiled and ran first time, and even the LEDs blink
as advertised, they just happen to match the LEDs on the SM1000!
+ However the speed was capped at about 130 kB/s. After a lot of
+ messing around I found suggestions in the comments from a similar
+ library here:
- When I fired up Minicom, it echoed characters:
+ http://stm32f4-discovery.com/2014/08/library-24-virtual-com-port-vcp-stm32f4xx/
- $ sudo minicom -D /dev/ttyACM0
+ The key was changing APP_RX_DATA_SIZE in usbd_conf.h to 10000. I
+ guess the previous size of 2048 was constraing the length of USB
+ packets, and the USB overhead meant slow throughput. I could
+ achieve a max of 450 kB/s with this change, about 1/3 of the
+ theoretical 1.5 MB/s max for USB FS (12 Mbit/s).
+
+ I used this to test grabbing data from the STM32F4 Discovery:
+ $ sudo dd if=/dev/ttyACM0 of=/dev/null count=100
+ 4+96 records in
+ 44+1 records out
+ 22615 bytes (23 kB) copied, 0.150884 s, 150 kB/s
+
+ However I occasionally see:
+ $ sudo dd if=/dev/ttyACM0 of=/dev/null count=100
+ dd: failed to open ‘/dev/ttyACM0’: Device or resource busy
+
+ Googling found some suggestion that this is due to "modem manager", however I
+ removed MM and the problem still exists.
\*---------------------------------------------------------------------------*/
#include "stm32f4_usb_vcp.h"
#include "sm1000_leds_switches.h"
-volatile uint32_t ticker, downTicker;
+volatile uint32_t ticker, buf_ticker;
+
+#define N 640*6
+
+short buf[N];
int main(void) {
+ int i;
+ for(i=0; i<N; i++)
+ buf[i] = 0;
+
sm1000_leds_switches_init();
usb_vcp_init();
SysTick_Config(SystemCoreClock/1000);
while (1) {
- /* Blink the orange LED at 1Hz */
+ /* Blink the discovery red LED at 1Hz */
- if (500 == ticker) {
+ if (ticker > 500) {
GPIOD->BSRRH = GPIO_Pin_13;
}
- else if (1000 == ticker) {
+ if (ticker > 1000) {
ticker = 0;
GPIOD->BSRRL = GPIO_Pin_13;
}
+ /* Every 40ms send a buffer, simulates 16 bit samples at Fs=96kHz */
- /* If there's data on the virtual serial port:
- * - Echo it back
- * - Turn the green LED on for 10ms
- */
- uint8_t theByte;
- if (VCP_get_char(&theByte)) {
- VCP_put_char(theByte);
-
-
- GPIOD->BSRRL = GPIO_Pin_12;
- downTicker = 10;
- }
- if (0 == downTicker) {
- GPIOD->BSRRH = GPIO_Pin_12;
+ if (buf_ticker > 40) {
+ buf_ticker = 0;
+ led_pwr(1);
+ VCP_send_buffer((uint8_t*)buf, sizeof(buf));
+ led_pwr(0);
}
+
}
return 0;
void SysTick_Handler(void)
{
ticker++;
- if (downTicker > 0)
- {
- downTicker--;
- }
+ buf_ticker++;
}