ECE 486 Support Libraries
init486.c
Go to the documentation of this file.
1 /*!
2  * @file
3  *
4  * @brief Processor configuration routines to support ECE 486
5  *
6  * @author Don Hummels
7  *
8  * @date Jan 2016
9  */
10 
11 /*
12  * Contains all of the subroutines to configure various peripherals of the stm32L476,
13  * such as RCC, GPIO Ports, DMAs, ADCs, DACs, etc.
14  *
15  * Rewrite taken from the STM32F4xx libraries in Jan 2016
16  *
17  * Aug 1, 2014:
18  * Major Revision:
19  * Drop support for the microphone.
20  * Add support for the STM32F429i-Discovery board
21  * Migrate from the "standard peripheral lib" to the newer "HAL" interface
22  *
23  * Dec 31, 2014:
24  * Add UART support for debugging Output
25  *
26  * Jan 5, 2015:
27  * Discarded stm32f4xx support. This version supports the STM32L476-Discovery
28  *
29  * Jan 2016 Major rewrite to port to the STM32L476G-Discovery board:
30  * All periferal assignments changed.
31  * New DMA/Timer interface for the L4xx processors
32  *
33  * Dec 2017 Modification to allow HSE Clock reference
34  *
35  *
36  */
37 
38 
39 #include <stdint.h>
40 #include <stdlib.h>
41 #include <string.h>
42 
43 #include "stm32l4xx_hal.h"
44 #include "stm32l476g_discovery.h"
45 #include "stm32l476g_discovery_glass_lcd.h"
46 
47 #include "sample486.h"
48 #include "init486.h"
49 #include "err486.h"
50 #include "usart.h"
51 #include "opamp486.h"
52 
53 /* Private functions ---------------------------------------------------------*/
54 static void blinkandwait(void);
55 static void ece_486_deinit(void);
56 static void SystemClock_Config_MSI(void);
57 static void SystemClock_Config_HSE(void);
58 static void init_dac(uint16_t fs, enum Num_Channels_Out chanout);
59 static void init_adc(uint16_t fs, enum Num_Channels_In chanin);
60 static void TIM4_Config(uint16_t fs);
61 static void config_digital_io(void);
62 static void config_pushbutton(void);
63 #ifdef TIM4_TO_PB6
64  static void config_clkout(void);
65 #endif
66 static HAL_StatusTypeDef Hacked_HAL_DAC_DualStart_DMA(
67  DAC_HandleTypeDef* hdac,
68  uint32_t* pData,
69  uint32_t Length);
70 
71 static DAC_HandleTypeDef DacHandle;
72 static ADC_HandleTypeDef AdcHandle;
73 static ADC_HandleTypeDef AdcHandle2;
74 
75 
76 /*
77  * Actual Sample Rate
78  */
80 
81 /*
82  * Global Variables
83  */
84 JOYState_TypeDef JoyState = JOY_NONE; // Can be JOY_NONE, JOY_UP, JOY_DOWN, JOY_RIGHT, JOY_LEFT
85 __IO FlagStatus KeyPressed = RESET;
86 
89 
90 /*
91  * Basic wrapper function to initialize peripherals for ECE 486 labs
92  * initialize() provided for backwards compatibility... uses the
93  * internal RC oscillator as the clock source.
94  */
95 void initialize(uint16_t fs,
96  enum Num_Channels_In chanin,
97  enum Num_Channels_Out chanout)
98 {
99  initialize_ece486(fs,chanin,chanout,MSI_INTERNAL_RC);
100 }
101 
102 /*
103  * Basic wrapper function to initialize peripherals for ECE 486 labs
104  */
105 void initialize_ece486(uint16_t fs,
106  enum Num_Channels_In chanin,
107  enum Num_Channels_Out chanout,
108  enum Clock_Reference clkref
109  )
110 {
111 
112  Output_Configuration = chanout;
113  Input_Configuration = chanin;
114 
115  // Initialize the HAL library
116  HAL_Init();
117 
118  // Shut down any dma/timer activity
119  ece_486_deinit();
120 
121  // Set up required LED output pins
122  BSP_LED_Init(ERROR_LED);
123  BSP_LED_Init(NORMAL_LED);
124 
125  // Set up the digital i/o pin
127 
128  // Configure the system clock
129  if (clkref == MSI_INTERNAL_RC) {
131  } else if (clkref == HSE_EXTERNAL_8MHz) {
133  } else {
135  }
136 
137  // Joystick Config
138  // BSP_JOY_Init(JOY_MODE_EXTI);
139  config_pushbutton(); // SEL (Center) Joystick input does not conflict with DAC/ADC
140 
141  // PB6 can be configured to show the sampling clock created by Timer4.
142  // (Commented out here to avoid a possible conflict with the I2C1 periferal, which is
143  // used by the CODEC on the discovery board)
144 #ifdef TIM4_TO_PB6
145  config_clkout();
146 #endif
147 
148  // LCD Config
149  BSP_LCD_GLASS_Init();
150 
151  // USART2 used to send printf() to the ST-Link serial port
152  usart_init();
153 
154 
155  // Rest until the user button is pressed to allow
156  // st-flash to reprogram without errors.
157  blinkandwait();
158 
159  // Calculate the actual sampling rate, and save it for access later
160  // through getsamplingfrequency(). (80 MHz clock, divided by the
161  // integer "fs" used to program the timers)
162  Actual_Sampling_Frequency = 80000000.0f / ((float) fs);
163 
164 
165  // request memory for the ADC and DAC DMA transfer buffers
166  // (These integer buffers store the raw ADC and DAC samples. The values
167  // are converted to float's before being handed to the user.)
168  ADC_Input_Buffer = (uint32_t *)malloc(sizeof(uint32_t)*ADC_Buffer_Size);
169  DAC_Output_Buffer = (uint32_t *)malloc(sizeof(uint32_t)*ADC_Buffer_Size);
170 
171  if (ADC_Input_Buffer==NULL || DAC_Output_Buffer==NULL ) {
173  while(1);
174  }
175 
176  // Error Handler
177  initerror(chanout);
178 
179  // Timer 4 generates the sampling clocks for the ADCs and DACs
180  TIM4_Config(fs);
181 
182  // DAC Config, including DMA2 Channel 5 to transfer samples to the DACs
183  init_dac(fs, chanout);
184 
185  // ADC Config, including DMA1 Channel 1 to transfer samples from the ADCs
186  init_adc(fs, chanin);
187 
188 }
189 
190 
191 
192 /**
193  * @brief System Clock Configuration
194  *
195  * Hummels Config to give 80 MHz SYSCLK, with PLL48M1CLK=48 MHz
196  * The system Clock is configured as follows :
197  * System Clock source = PLL (MSI)
198  * SYSCLK(Hz) = 80000000
199  * HCLK(Hz) = 80000000
200  * AHB Prescaler = 1
201  * APB1 Prescaler = 1
202  * APB2 Prescaler = 1
203  * MSI Frequency(Hz) = 16000000 (MSI Range=8)
204  * PLL_M = 2
205  * PLL_N = 20
206  * PLL_R = 2 ( SYSCLK = PLLCLK = (16MHz)*N/M/R = 80 MHz )
207  * PLL_P = 7 (No reason for this...)
208  * PLL_Q = 5 ( PLL48M1CLK = (16MHz)(N/M/Q) = 32 MHz ?! )
209  * Flash Latency(WS) = 4
210  *
211  * HSE: Not assumed active (xtal not populated on STM32L476G-Discovery)
212  * MSI: 16 MHz (multiple speed internal RC Oscillator, configured using Range=8)
213  * HSI: 16 MHz (Fixed frequency high speed internal Oscillator)
214  * LSI: 32.768 kHz (Low freq internal oscillator)
215  * ( MSI *N/M = 160 MHz )
216  * SYSCLK: 80 MHz (= 160/R)
217  * HCLK (AHB Bus, Core, Memory, and DMA): 80 MHz (Max value)
218  * PCLK1 (APB1 Periferals, some USARTs and Timers): 80 MHz (Max value)
219  * PCLK2 (APB2 Periferals, some USARTs and Timers): 80 MHz (Max value)
220  *
221  * (ALTERNATIVE??? Use the HSI as the reference oscillator. Change N to 15.)
222  *
223  * @param None
224  * @retval None
225  */
227 {
228  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
229  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
230 
231  /* MSI is enabled after System reset, activate PLL with MSI as source */
232  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
233  RCC_OscInitStruct.MSIState = RCC_MSI_ON;
234  RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_8;
235  RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
236 
237  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
238  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
239  RCC_OscInitStruct.PLL.PLLM = 2;
240  RCC_OscInitStruct.PLL.PLLN = 20;
241  RCC_OscInitStruct.PLL.PLLR = 2;
242  RCC_OscInitStruct.PLL.PLLP = 7;
243  RCC_OscInitStruct.PLL.PLLQ = 5;
244  if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
245  {
246  /* Initialization Error */
247  while(1);
248  }
249 
250  /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
251  clocks dividers */
252  RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
253  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
254  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
255  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
256  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
257  if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
258  {
259  /* Initialization Error */
260  while(1);
261  }
262 }
263 
264 /**
265  * @brief Hummels Config to give 80 MHz SYSCLK, with PLL48M1CLK=48 MHz
266  * The system Clock is configured as follows :
267  * System Clock source = PLL (HSE)
268  * SYSCLK(Hz) = 80000000
269  * HCLK(Hz) = 80000000
270  * AHB Prescaler = 1
271  * APB1 Prescaler = 1
272  * APB2 Prescaler = 1
273  * HSE Frequency(Hz) = 8000000 (8 MHz external reference expected)
274  * PLL_M = 1
275  * PLL_N = 20
276  * PLL_R = 2 ( SYSCLK = PLLCLK = (8MHz)*N/M/R = 80 MHz )
277  * PLL_P = 7 (No reason for this...)
278  * PLL_Q = 5 ( PLL48M1CLK = (16MHz)(N/M/Q) = 32 MHz !?! )
279  * Flash Latency(WS) = 4
280  *
281  * HSE: Driven by 8 MHz ref clock (Requires jumper changes on STM32L476G-Discovery)
282  * MSI: Not configured here
283  * HSI: 16 MHz (Fixed frequency high speed internal Oscillator)
284  * LSI: 32.768 kHz (Low freq internal oscillator)
285  * ( PLL Input = HSE*N/M = 160 MHz )
286  * SYSCLK: 80 MHz (= 160/R)
287  * HCLK (AHB Bus, Core, Memory, and DMA): 80 MHz (Max value)
288  * PCLK1 (APB1 Periferals, some USARTs and Timers): 80 MHz (Max value)
289  * PCLK2 (APB2 Periferals, some USARTs and Timers): 80 MHz (Max value)
290  *
291  * @retval None
292  */
294 {
295  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
296  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
297 
298  /* Enable HSE Oscillator and activate PLL with HSE as source */
299  /* (Default MSI Oscillator enabled at system reset remains ON) */
300  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
301  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
302  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
303  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
304  RCC_OscInitStruct.PLL.PLLM = 1;
305  RCC_OscInitStruct.PLL.PLLN = 20;
306  RCC_OscInitStruct.PLL.PLLR = 2;
307  RCC_OscInitStruct.PLL.PLLP = 7;
308  RCC_OscInitStruct.PLL.PLLQ = 5;
309  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
310  {
311  /* Initialization Error */
312  while(1);
313  }
314 
315  /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
316  clocks dividers */
317  RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
318  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
319  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
320  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
321  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
322  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
323  {
324  /* Initialization Error */
325  while(1);
326  }
327 }
328 
329 static void ece_486_deinit(void)
330 {
331  static DMA_HandleTypeDef hdma;
332  static TIM_HandleTypeDef htim;
333 
334  DacHandle.Instance = DAC;
335  HAL_DAC_DeInit(&DacHandle);
336 
337  htim.Instance = TIM4;
338  HAL_TIM_Base_DeInit(&htim);
339 
340  AdcHandle.Instance = ADC1;
341  HAL_ADC_DeInit(&AdcHandle);
342  AdcHandle2.Instance = ADC2;
343  HAL_ADC_DeInit(&AdcHandle);
344 
345  hdma.Instance = DMA2_Channel5;
346  HAL_DMA_DeInit(&hdma);
347  hdma.Instance = DMA1_Channel1;
348  HAL_DMA_DeInit(&hdma);
349 
350 
351 }
352 
353 /**
354  * @brief Configure a digital output pin
355  * @param None
356  * @retval None
357  */
358 static void config_digital_io(void)
359 {
360  GPIO_InitTypeDef my_io_pin;
361 
362  DIGITAL_IO_CLK_ENABLE();
363  my_io_pin.Pin = DIGITAL_IO_PIN; // Pin 4
364  my_io_pin.Mode = GPIO_MODE_OUTPUT_PP; // Push/Pull digital output
365  my_io_pin.Pull = GPIO_NOPULL; // No pullup or pulldown resistor
366  my_io_pin.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // LOW, MEDIUM, FAST, or HIGH
367  HAL_GPIO_Init(DIGITAL_IO_PORT, &my_io_pin);
368 }
369 
370 #ifdef TIM4_TO_PB6
371 /**
372  * @brief Configure PB6 to show the sampling clock
373  * @param None
374  * @retval None
375  */
376 static void config_clkout(void)
377 {
378  GPIO_InitTypeDef my_io_pin;
379 
380  __HAL_RCC_GPIOB_CLK_ENABLE();
381  my_io_pin.Pin = GPIO_PIN_6; // Pin 4
382  my_io_pin.Mode = GPIO_MODE_AF_PP; // Push/Pull digital output
383  my_io_pin.Pull = GPIO_NOPULL; // No pullup or pulldown resistor
384  my_io_pin.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // LOW, MEDIUM, FAST, or HIGH
385  my_io_pin.Alternate = GPIO_AF2_TIM4;
386  HAL_GPIO_Init(DIGITAL_IO_PORT, &my_io_pin);
387 }
388 #endif
389 
390 /**
391  * @brief Configure PA6 (Joystic Center) generate an interrupt
392  * @param None
393  * @retval None
394  */
395 static void config_pushbutton(void)
396 {
397 
398  GPIO_InitTypeDef joy_center;
399 
400  __HAL_RCC_GPIOA_CLK_ENABLE();
401  joy_center.Pin = GPIO_PIN_0; // Pin 4
402  joy_center.Mode = GPIO_MODE_IT_FALLING; // Push/Pull digital output
403  joy_center.Pull = GPIO_PULLDOWN; // No pullup or pulldown resistor
404  joy_center.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // LOW, MEDIUM, FAST, or HIGH
405  HAL_GPIO_Init(GPIOA, &joy_center);
406  HAL_NVIC_SetPriority( (IRQn_Type) EXTI0_IRQn, 0x0F, 0x00);
407  HAL_NVIC_EnableIRQ((IRQn_Type) EXTI0_IRQn);
408 }
409 
410 
411 
412 /**
413  * @brief Wait for a button-press while blinking the LEDs
414  * @param None
415  * @retval None
416  */
417 static void blinkandwait(void) {
418 
419  BSP_LED_Off(NORMAL_LED);
420  BSP_LED_On(ERROR_LED);
421 
422  BSP_LCD_GLASS_DisplayString((uint8_t*) "ECE486");
423  KeyPressed = RESET; // Reset the button status
424  while (KeyPressed == RESET) { // Keep blinking until button press
425  BSP_LED_Toggle(NORMAL_LED);
426  HAL_Delay(100);
427  BSP_LED_Toggle(ERROR_LED);
428  HAL_Delay(100);
429  }
430 
431  BSP_LED_On(NORMAL_LED);
432  BSP_LED_Off(ERROR_LED);
433  KeyPressed=RESET; // Reset the button status
434  BSP_LCD_GLASS_DisplayString((uint8_t*) "RUN ");
435  HAL_Delay(300);
436 }
437 
438 /*
439  * Simple function to return the best guess at the actual sampling frequency.
440  */
443 }
444 
445 
446 /**********************************************************************************/
447 /* ******** DAC Initialization Code (and associated DMAs, Timers, IO pins) ***** */
448 /**********************************************************************************/
449 
450 /*
451  * DAC Set-up
452  * Configure and enable the appropriate output gpio pins (analog outputs)
453  * Configure the DMA to transfer samples from the DAC output buffer to the DAC
454  * Turn on the DAC and DMA clocks
455  * Start the DAC and DMA
456  */
457 static void init_dac(uint16_t fs, enum Num_Channels_Out chanout)
458 {
459  static DAC_ChannelConfTypeDef DacConfig;
460 
461  DacHandle.Instance = DAC;
462 
463  /*
464  * DAC Channel 2... Trigger with Timer 4 TRG0 Event
465  * Note that HAL_DAC_Init() calls HAL_DAC_MspInit() (The MPU-specific initialization
466  * routine). This function must be re-defined below in order to associate
467  * the correct DMA stream with the DAC. Similarly, HAL_DAC_DeInit() calls
468  * HAL_DAC_MspDeInit()... which we're on the hook to define.
469  */
470  if(HAL_DAC_Init(&DacHandle) != HAL_OK) flagerror(DAC_CONFIG_ERROR);
471 
472  DacConfig.DAC_Trigger = DAC_TRIGGER_T4_TRGO;
473  DacConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; // Output Buffer Amp
474  DacConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
475  DacConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE;
476  DacConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
477 
478  if (chanout == MONO_OUT) {
479  if(HAL_DAC_ConfigChannel(&DacHandle, &DacConfig, DAC_CHANNEL_2) != HAL_OK)
481 
482  if( HAL_DAC_Start_DMA(&DacHandle,
483  DAC_CHANNEL_2,
484  (uint32_t *)DAC_Output_Buffer,
486  DAC_ALIGN_12B_L)
487  != HAL_OK) flagerror(DAC_CONFIG_ERROR);
488 
489  } else if (chanout == STEREO_OUT) {
490 
491  // DAC Channel 2 is the same as the Mono case... Channel 2 drives PA5 directly
492  if(HAL_DAC_ConfigChannel(&DacHandle, &DacConfig, DAC_CHANNEL_2) != HAL_OK)
494 
495  // DAC Channel 1 is directed to opamp1, which can drive PA3
496  // (This is because PA4 is not extended to the connector on the discovery board,
497  // and PA3 is extended. The opamp is used to get the dac stignal to PA3.)
498  opamp_init();
499  DacConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_ENABLE;
500  if(HAL_DAC_ConfigChannel(&DacHandle, &DacConfig, DAC_CHANNEL_1) != HAL_OK)
502 
503  /*
504  * To start the DAC in dual-sample mode, use a hacked up version of
505  * HAL_DAC_Start_DMA()... The HAL library does not seem to support the
506  * dual mode very well!
507  */
509  (uint32_t *)DAC_Output_Buffer,
511  != HAL_OK) flagerror(DAC_CONFIG_ERROR);
512  } else {
514  }
515 
516 }
517 
518 
519 /*
520  * Hacked up version of HAL_DAC_START_DMA intended to support dual-sample mode
521  * for the DAC.
522  *
523  * Data is assumed left aligned for dual-sample mode.
524  */
525 static HAL_StatusTypeDef Hacked_HAL_DAC_DualStart_DMA(
526  DAC_HandleTypeDef* hdac,
527  uint32_t* pData,
528  uint32_t Length)
529 {
530  uint32_t tmpreg = 0;
531 
532  /* Process locked */
533  __HAL_LOCK(hdac);
534 
535  /* Change DAC state */
536  hdac->State = HAL_DAC_STATE_BUSY;
537 
538  /* Set the DMA transfer complete callback for channel2 */
539  hdac->DMA_Handle2->XferCpltCallback = DAC_DMAConvCpltCh2;
540 
541  /* Set the DMA half transfer complete callback for channel2 */
542  hdac->DMA_Handle2->XferHalfCpltCallback = DAC_DMAHalfConvCpltCh2;
543 
544  /* Set the DMA error callback for channel2 */
545  hdac->DMA_Handle2->XferErrorCallback = DAC_DMAErrorCh2;
546 
547  /* Enable the selected DAC channel2 DMA request */
548  hdac->Instance->CR |= DAC_CR_DMAEN2;
549 
550  tmpreg = (uint32_t)&hdac->Instance->DHR12LD; // 12-bit left align, dual sample mode
551 
552  /* Enable the DMA Stream */
553  HAL_DMA_Start_IT(hdac->DMA_Handle2, (uint32_t)pData, tmpreg, Length);
554 
555  /* Enable the both DACs */
556  __HAL_DAC_ENABLE(hdac, DAC_CHANNEL_2);
557  __HAL_DAC_ENABLE(hdac, DAC_CHANNEL_1);
558 
559  /* Process Unlocked */
560  __HAL_UNLOCK(hdac);
561 
562  /* Return function status */
563  return HAL_OK;
564 }
565 
566 /*
567  * MPU Specific DAC Initialization code
568  * Set up DMA2, Channel 5 to transfer data to DAC2 based upon interrupt
569  * from DAC2 (DAC2 uses GPIO Pin PA5... Need the GPIOA Clock enabled)
570  */
571 void HAL_DAC_MspInit(DAC_HandleTypeDef* hdac) {
572  GPIO_InitTypeDef gpio_dacpins;
573  static DMA_HandleTypeDef hdma;
574 
575  // Start required clocks...
576  __HAL_RCC_DAC1_CLK_ENABLE(); /* DAC Periph clock enable */
577  __HAL_RCC_GPIOA_CLK_ENABLE(); /* Enable GPIO clock for PA5 */
578  __HAL_RCC_DMA2_CLK_ENABLE(); /* DMA2 clock enable */
579 
580  // Configure PA5 as the output for DAC Channel 2.
581  gpio_dacpins.Pin = GPIO_PIN_5;
582  gpio_dacpins.Mode = GPIO_MODE_ANALOG; /* Analog output pin */
583  gpio_dacpins.Pull = GPIO_NOPULL; /* Don't activate pull up/down resistor */
584  gpio_dacpins.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
585  HAL_GPIO_Init(GPIOA, &gpio_dacpins);
586 
587  // Initialize DMA2, Channel 5 for this DAC Channel 2 (DMA Request 3))
588  hdma.Instance = DMA2_Channel5;
589  hdma.Init.Request = DMA_REQUEST_3;
590  hdma.Init.Direction = DMA_MEMORY_TO_PERIPH;
591  hdma.Init.PeriphInc = DMA_PINC_DISABLE;
592  hdma.Init.MemInc = DMA_MINC_ENABLE;
593  hdma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
594  hdma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
595  hdma.Init.Mode = DMA_CIRCULAR;
596  hdma.Init.Priority = DMA_PRIORITY_HIGH;
597  HAL_DMA_Init(&hdma);
598 
599  // Associate the initialized DMA handle to DAC channel 2
600  // (strange, multi-statement macro)
601  __HAL_LINKDMA(hdac, DMA_Handle2, hdma);
602 
603  // Configure the NVIC for DMA
604  HAL_NVIC_SetPriority(DMA2_Channel5_IRQn, 1, 0);
605  // Enabling the interrupt here will call ISR DMA1_Stream6_IRQHandler()
606  // on buffer transfer half-complete and complete.
607  // HAL_NVIC_EnableIRQ(DMA2_Channel5_IRQn);
608 
609  // Configure Interrupt for DAC Channel 2
610  HAL_NVIC_SetPriority(TIM6_DAC_IRQn,3,0);
611  HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
612 
613 
614 }
615 
616 void HAL_DAC_MspDeInit(DAC_HandleTypeDef* hdac) {
617 
618  // Reset and release the DAC peripheral
619  __HAL_RCC_DAC1_FORCE_RESET();
620  __HAL_RCC_DAC1_RELEASE_RESET();
621 
622 
623  // De-Initialize the DMA Stream
624  HAL_DMA_DeInit(hdac->DMA_Handle2);
625 
626  // Disable the NVIC for DMA
627  HAL_NVIC_DisableIRQ(DMA2_Channel5_IRQn);
628  HAL_NVIC_DisableIRQ(TIM6_DAC_IRQn);
629 }
630 
631 /*
632  * MPU Specific Timer initialization and de-initialization code
633  * (called by HAL_TIM_Base_Init()
634  */
635 void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim) {
636  if(htim->Instance == TIM4) {
637  __HAL_RCC_TIM4_CLK_ENABLE();
638  } else {
639 
640  }
641 }
642 
643 
644 void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef *htim) {
645  if(htim->Instance == TIM4) {
646  __HAL_RCC_TIM4_FORCE_RESET();
647  __HAL_RCC_TIM4_RELEASE_RESET();
648  } else {
649 
650  }
651 }
652 
653 
654 
655 
656 
657 
658 
659 
660 /**********************************************************************************/
661 /* ******** ADC Initialization Code (and associated DMAs, Timers, IO pins) ***** */
662 /**********************************************************************************/
663 
664 /*
665  * ADC Initialization
666  *
667  * ADC1 is always started, with DMA1, Channel 1 transferring samples
668  * to the input buffer. ADC1 is triggered through Timer 4.
669  *
670  * If chanin=STEREO_IN, ADC2 is also configured as a slave to ADC1 (no
671  * external trigger required). The DMA configuration is changed to use
672  * dual-sample mode so that it serves data from both ADCs
673  */
674 static void init_adc(uint16_t fs, enum Num_Channels_In chanin)
675 {
676  ADC_ChannelConfTypeDef sConfig;
677  ADC_MultiModeTypeDef mode;
678 
679  // ADC1 is configured the same, whether there's one or two channels...
680  // In 2-channel mode, The ADC1 clock is distributed to ADC2, which acts as the
681  // slave.
682  // ADC1 is triggered using Timer 4.
683  // DMA1, Channel 1 is used to transfer data from the ADC to an internal buffer.
684  AdcHandle.Instance = ADC1;
685 
686  AdcHandle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
687  AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;
688  AdcHandle.Init.ScanConvMode = DISABLE;
689  AdcHandle.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
690  AdcHandle.Init.ContinuousConvMode = DISABLE;
691  AdcHandle.Init.NbrOfConversion = 1;
692  AdcHandle.Init.DiscontinuousConvMode = DISABLE;
693  AdcHandle.Init.NbrOfDiscConversion = 1;
694  AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
695  // The ADC_EXTERNALTRIG_T4_TRGO macro seems to be defined (in error) to
696  // specify CC4 envents instead of TRGO events. I'll use the low-level
697  // values from the HAL library (should not be needed!)
698  // AdcHandle.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T4_TRGO;
699  AdcHandle.Init.ExternalTrigConv = LL_ADC_REG_TRIG_EXT_TIM4_TRGO;
700  AdcHandle.Init.DataAlign = ADC_DATAALIGN_LEFT;
701  AdcHandle.Init.DMAContinuousRequests = ENABLE;
702  AdcHandle.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
703  AdcHandle.Init.OversamplingMode = DISABLE;
704 
705  if(HAL_ADC_Init(&AdcHandle) != HAL_OK) flagerror(ADC_CONFIG_ERROR);
706  if(HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_SINGLE_ENDED) != HAL_OK) flagerror(ADC_CONFIG_ERROR);
707 
708  sConfig.Channel = ADC1_CHANNEL;
709  sConfig.Rank = ADC_REGULAR_RANK_1;
710  sConfig.SamplingTime = ADC_SAMPLETIME_92CYCLES_5;
711  sConfig.SingleDiff = ADC_SINGLE_ENDED;
712  sConfig.OffsetNumber = ADC_OFFSET_NONE;
713  sConfig.Offset = 0;
714 
715  if(HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK) flagerror(ADC_CONFIG_ERROR);
716 
717  if (chanin == MONO_IN) {
718  // Start up ADC1 with the DMA, and we're done
719  if(HAL_ADC_Start_DMA(&AdcHandle, (uint32_t *)ADC_Input_Buffer, ADC_Buffer_Size) != HAL_OK) {
721  }
722 
723  } else if (chanin == STEREO_IN) {
724  // Two Analog Inputs... ADC2 is the slave...
725  // ADC2 gets the same configuration without the external trigger.
726  // (it's clocked as a slave to ADC1)
727  AdcHandle2.Instance = ADC2;
728  AdcHandle2.Init = AdcHandle.Init; // Copy the ADC1 Configuration
729  AdcHandle2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
730 
731  if(HAL_ADC_Init(&AdcHandle2) != HAL_OK) flagerror(ADC_CONFIG_ERROR);
732 
733  sConfig.Channel = ADC2_CHANNEL; //
734 
735  if(HAL_ADC_ConfigChannel(&AdcHandle2, &sConfig) != HAL_OK) flagerror(ADC_CONFIG_ERROR);
736 
737  // Multimode Configuration (Common ADC configuration register... only call once
738  // using either ADC handle)
739  mode.Mode = ADC_DUALMODE_REGSIMULT;
740  mode.DMAAccessMode = ADC_DMAACCESSMODE_12_10_BITS;
741  mode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_5CYCLES; // Ignored? (Since not interleaved)
742  if(HAL_ADCEx_MultiModeConfigChannel(&AdcHandle, &mode) != HAL_OK) flagerror(ADC_CONFIG_ERROR);
743 
744  // Normal start for ADC2 (Slave Channel)
745  if(HAL_ADC_Start(&AdcHandle2) != HAL_OK) flagerror(ADC_CONFIG_ERROR);
746 
747  // ADC1 gets the multi-mode start, which fires up the DMA.
748  if(HAL_ADCEx_MultiModeStart_DMA(&AdcHandle, (uint32_t *)ADC_Input_Buffer, ADC_Buffer_Size) != HAL_OK) {
750  }
751  }
752 }
753 
754 
755 static void TIM4_Config(uint16_t fs)
756 {
757  static TIM_HandleTypeDef htim;
758  TIM_MasterConfigTypeDef sMasterConfig;
759 
760  htim.Instance = TIM4;
761 
762  /* Number of timer counts per ADC sample.... The timer 4 clock frequency is
763  * the APB1 Bus frequency: 80 MHz. For example, To get 50 ksps, we're
764  * counting (50 MHz)/(50 ksps) = 1600 counts
765  */
766  htim.Init.Period = fs;
767  htim.Init.Prescaler = 0;
768  htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
769  htim.Init.CounterMode = TIM_COUNTERMODE_UP;
770  HAL_TIM_Base_Init(&htim);
771 
772  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
773  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
774 
775  HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);
776 
777  /*##-2- Enable TIM peripheral counter ######################################*/
778  HAL_TIM_Base_Start(&htim);
779 }
780 
781 /*
782  * MPU Specific ADC Initialization code.
783  * For ADC1, initialize the DMA, and associated with the ADC.
784  * For ADC2, don't bother with the DMA... The ADC1 DMA will run in dual-sample
785  * mode and transfer both the ADC1 and ADC2 samples
786  * Set up DMA1, Channel1 to transfer the data triggered from ADC1 (Request 0)
787  * Enable the half-transfer and full-transfer interrupts from the DMA
788  */
789 void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
790 {
791  GPIO_InitTypeDef gpio_adcpins;
792  static DMA_HandleTypeDef hdma;
793 
794  __HAL_RCC_ADC_CLK_ENABLE();
795  __HAL_RCC_ADC_CONFIG(RCC_ADCCLKSOURCE_SYSCLK);
796 
797  if (hadc->Instance == ADC2) { /* Slave ADC... no DMA/Trigger required */
798  /*
799  * ADC2 has no associated DMA... just activate the clocks, and enable the
800  * appropriate GPIO output pins as analog I/O without pull-up or pull-down resistors
801  */
802  ADC2_GPIO_CLK_ENABLE();
803 
804  gpio_adcpins.Pin = ADC2_GPIO_PIN;
805  gpio_adcpins.Mode = GPIO_MODE_ANALOG_ADC_CONTROL; /* Analog output pin */
806  gpio_adcpins.Pull = GPIO_NOPULL; /* Don't activate pull up/down resistor */
807  HAL_GPIO_Init(ADC2_GPIO_PORT, &gpio_adcpins);
808  } else { /* Master ADC */
809  /*
810  * ADC1 needs clocks and gpio pins... but also has a DMA associated with it.
811  * The DMA is started in dual-sample mode if two channels are used (so it
812  * simultaneously transfers the ADC2 samples.
813  */
814  ADC1_GPIO_CLK_ENABLE();
815  __HAL_RCC_DMA1_CLK_ENABLE();
816 
817  gpio_adcpins.Pin = ADC1_GPIO_PIN;
818  gpio_adcpins.Mode = GPIO_MODE_ANALOG_ADC_CONTROL; /* Analog input pin */
819  gpio_adcpins.Pull = GPIO_NOPULL;
820  HAL_GPIO_Init(ADC1_GPIO_PORT, &gpio_adcpins);
821 
822  // DMA1, Channel 1 serves the ADC1 Trigger
823  hdma.Instance = DMA1_Channel1;
824  hdma.Init.Request = DMA_REQUEST_0;
825  hdma.Init.Direction = DMA_PERIPH_TO_MEMORY;
826  hdma.Init.PeriphInc = DMA_PINC_DISABLE;
827  hdma.Init.MemInc = DMA_MINC_ENABLE;
828  hdma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
829  hdma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
830  hdma.Init.Mode = DMA_CIRCULAR;
831  hdma.Init.Priority = DMA_PRIORITY_HIGH;
832 
833  HAL_DMA_Init(&hdma);
834 
835  /* Associate the initialized DMA handle to the the ADC handle */
836  __HAL_LINKDMA(hadc, DMA_Handle, hdma);
837 
838  // Configure and enable the interrupts from the DMA
839  // ISR is DMA1_Channel1_IRQHandler()
840  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
841  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
842 
843  }
844 
845  // Configure and enable the interrupts from the ADC1 & for ADC2
846  // ( Both ADCs share the same isr on the STM32L4xx )
847  // ISR is ADC1_2_IRQHandler()
848  HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0);
849  HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
850 
851 }
852 
853 void HAL_ADC_MspDeInit(ADC_HandleTypeDef *hadc)
854 {
855  __ADC_FORCE_RESET();
856  __ADC_RELEASE_RESET();
857 
858  if(hadc->DMA_Handle != NULL) HAL_DMA_DeInit(hadc->DMA_Handle);
859 
860  HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);
861  HAL_NVIC_DisableIRQ(ADC1_2_IRQn);
862 }
863 
864 
865 
866 
867 
868 /*
869  * ISR Routines
870  */
871 
873 {
874  HAL_DMA_IRQHandler(DacHandle.DMA_Handle2);
875 }
876 
878 {
879  HAL_DAC_IRQHandler(&DacHandle);
880 }
881 
883 {
884  /*
885  * Note that ADC1 and ADC2 share the same ISR
886  */
887  HAL_ADC_IRQHandler(&AdcHandle);
888  HAL_ADC_IRQHandler(&AdcHandle2);
889 }
890 
892 {
893  /*
894  * The HAL DMA Handler routine examines the interrupt, and eventually
895  * calls HAL_ADC_ConvCpltCallback() or HAL_ADC_ConvHalfCpltCallback()
896  * as appropriate.
897  * These callback routines by default do nothing... but are declared as
898  * __weak so they can be redefined below to do the right thing.
899  */
900  HAL_DMA_IRQHandler(AdcHandle.DMA_Handle);
901 }
902 
903 
904 // This function handles SysTick Handler.
905 void SysTick_Handler(void)
906 {
907  HAL_IncTick();
908 }
909 
910 
911 
912 // Joystick presses should generate an EXTIx interrupt
914 {
915  HAL_GPIO_EXTI_IRQHandler(SEL_JOY_PIN);
916 }
917 
919 {
920  HAL_GPIO_EXTI_IRQHandler(LEFT_JOY_PIN);
921 }
922 
924 {
925  HAL_GPIO_EXTI_IRQHandler(RIGHT_JOY_PIN);
926 }
927 
929 {
930  HAL_GPIO_EXTI_IRQHandler(UP_JOY_PIN);
931 }
932 
934 {
935  HAL_GPIO_EXTI_IRQHandler(DOWN_JOY_PIN);
936 }
937 
938 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
939 
940  // Check for Joystick press
941  if ( GPIO_Pin & (DOWN_JOY_PIN | UP_JOY_PIN | RIGHT_JOY_PIN | LEFT_JOY_PIN | SEL_JOY_PIN) ){
942  KeyPressed = SET;
943  switch(GPIO_Pin) {
944  case DOWN_JOY_PIN :
945  JoyState = JOY_DOWN;
946  break;
947  case UP_JOY_PIN :
948  JoyState = JOY_UP;
949  break;
950  case SEL_JOY_PIN :
951  JoyState = JOY_SEL;
952  break;
953  case RIGHT_JOY_PIN :
954  JoyState = JOY_RIGHT;
955  break;
956  case LEFT_JOY_PIN :
957  JoyState = JOY_LEFT;
958  break;
959  }
960  } else {
961  // Not a joystick event... what else generates exti interrupts??
962  }
963 }
964 
#define NORMAL_LED
Green LED, STM32L476G-Discovery.
Definition: init486.h:40
JOYState_TypeDef JoyState
Definition: init486.c:84
static void TIM4_Config(uint16_t fs)
Definition: init486.c:755
volatile uint32_t * ADC_Input_Buffer
Buffer to store samples transfered from the ADC by a DMA.
Definition: sample486.c:21
Stereo Output: Configure DAC1 and DAC2, dual DMA Transfer.
Definition: init486.h:155
void HAL_DAC_MspDeInit(DAC_HandleTypeDef *hdac)
Definition: init486.c:616
static void init_adc(uint16_t fs, enum Num_Channels_In chanin)
Definition: init486.c:674
static void SystemClock_Config_HSE(void)
Hummels Config to give 80 MHz SYSCLK, with PLL48M1CLK=48 MHz The system Clock is configured as follow...
Definition: init486.c:293
void EXTI0_IRQHandler(void)
Definition: init486.c:913
void ADC1_2_IRQHandler(void)
Definition: init486.c:882
void initerror()
Initialization routine to set up error buffers.
Definition: err486.c:34
Mono Input: Only configure ADC1, single DMA Transfer.
Definition: init486.h:145
#define CLOCK_CONFIG_ERROR
Definition: err486.h:63
enum Num_Channels_Out Output_Configuration
Definition: init486.c:87
static void ece_486_deinit(void)
Definition: init486.c:329
void EXTI9_5_IRQHandler(void)
Definition: init486.c:933
void usart_init(void)
USART Configuration to talk to ST-LINK.
Definition: usart.c:22
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef *htim)
Definition: init486.c:644
static void config_pushbutton(void)
Configure PA6 (Joystic Center) generate an interrupt.
Definition: init486.c:395
static void SystemClock_Config_MSI(void)
System Clock Configuration.
Definition: init486.c:226
static DAC_HandleTypeDef DacHandle
Definition: init486.c:71
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
Definition: init486.c:789
Internal MSI RC Oscillator.
Definition: init486.h:177
volatile uint32_t * DAC_Output_Buffer
Buffer to hold samples to be transfered to the DAC(s) by a DMA.
Definition: sample486.c:22
Num_Channels_In
Number of input audio channels.
Definition: init486.h:144
Clock_Reference
Clock Reference Source.
Definition: init486.h:176
__IO FlagStatus KeyPressed
Definition: init486.c:85
enum Num_Channels_In Input_Configuration
Definition: init486.c:88
uint32_t ADC_Buffer_Size
Total buffer size being filled by DMA for ADC/DAC.
Definition: sample486.c:28
float getsamplingfrequency(void)
Simple function to return the best guess at the actual sampling frequency.
Definition: init486.c:441
void SysTick_Handler(void)
Definition: init486.c:905
void flagerror(int errorcode)
Records and indicates an error condition.
Definition: err486.c:48
#define DAC_CONFIG_ERROR
Definition: err486.h:59
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
Definition: init486.c:635
void DMA1_Channel1_IRQHandler(void)
Definition: init486.c:891
void opamp_init(void)
Set up OPAMP1 to buffer DAC Channel 1 to drive PA3.
Definition: opamp486.c:19
static void config_digital_io(void)
Configure a digital output pin.
Definition: init486.c:358
#define ADC_CONFIG_ERROR
Definition: err486.h:60
static ADC_HandleTypeDef AdcHandle
Definition: init486.c:72
void DMA2_Channel5_IRQHandler(void)
Definition: init486.c:872
USART Initialization to stream printf() output back to the ST-LINK.
void initialize(uint16_t fs, enum Num_Channels_In chanin, enum Num_Channels_Out chanout)
Wrapper function to perform all processor initialization for ECE 486.
Definition: init486.c:95
void TIM6_DAC_IRQHandler(void)
Definition: init486.c:877
void initialize_ece486(uint16_t fs, enum Num_Channels_In chanin, enum Num_Channels_Out chanout, enum Clock_Reference clkref)
Wrapper function to perform all processor initialization for ECE 486.
Definition: init486.c:105
void HAL_ADC_MspDeInit(ADC_HandleTypeDef *hadc)
Definition: init486.c:853
void EXTI3_IRQHandler(void)
Definition: init486.c:928
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
Definition: init486.c:938
static void blinkandwait(void)
Wait for a button-press while blinking the LEDs.
Definition: init486.c:417
ECE 486 Interface fuctions to manipulate blocks of sampled data.
#define MEMORY_ALLOCATION_ERROR
malloc() or calloc() returned NULL
Definition: err486.h:58
Error Handling for ECE 486 STM32L476G-Discovery Interface.
static HAL_StatusTypeDef Hacked_HAL_DAC_DualStart_DMA(DAC_HandleTypeDef *hdac, uint32_t *pData, uint32_t Length)
Definition: init486.c:525
STM32L476-Discovery configuration routines to support ECE 486.
#define ERROR_LED
Red LED, STM32L476G-Discovery.
Definition: init486.h:39
External 8MHz reference.
Definition: init486.h:178
Mono Output: Only configure DAC1, single DMA Transfer.
Definition: init486.h:154
void HAL_DAC_MspInit(DAC_HandleTypeDef *hdac)
Definition: init486.c:571
static float Actual_Sampling_Frequency
Definition: init486.c:79
Num_Channels_Out
Number of output audio channels.
Definition: init486.h:153
void EXTI2_IRQHandler(void)
Definition: init486.c:923
static void init_dac(uint16_t fs, enum Num_Channels_Out chanout)
Definition: init486.c:457
void EXTI1_IRQHandler(void)
Definition: init486.c:918
Initialization of OPAMP1 to buffer DAC Ch 1 to an output pin.
Stereo Input: Configure ADC1 and ADC2, dual DMA Transfer.
Definition: init486.h:146
static ADC_HandleTypeDef AdcHandle2
Definition: init486.c:73