ECE 486 Support Libraries
sample486.c
Go to the documentation of this file.
1 /*!
2  * @file
3  *
4  * @brief Implementation of user sampler interface functions
5  * @author Don Hummels
6  * @date Jan 2016
7  */
8 
9 
10 #include "stm32l4xx_hal.h"
11 #include "stm32l476g_discovery.h"
12 
13 #include <stdlib.h>
14 #include "sample486.h"
15 #include "err486.h"
16 #include "arm_math.h"
17 
18 /*
19  * Global data buffers which are being filled/emptied by the DMAs
20  */
21 volatile uint32_t *ADC_Input_Buffer=NULL;
22 volatile uint32_t *DAC_Output_Buffer=NULL;
23 
24 /*
25  * Data Block sizes for streamed ADC/DAC data
26  */
27 uint32_t ADC_Block_Size = DEFAULT_BLOCKSIZE; //!< Number of samples user accesses per data block
28 uint32_t ADC_Buffer_Size = 2*DEFAULT_BLOCKSIZE; //!< Total buffer size being filled by DMA for ADC/DAC
29 
30 /*
31  * Sampler_Status indicates whether the user is working on a buffer of data,
32  * or has finished working on the buffer and is waiting for the next buffer to
33  * arrive. The variable is used in the interrupt service routine to detect
34  * whether a "buffer overrun" has occurred.
35  */
37 
38 /*
39  * The ISR also sets the Lower_Ready flag to indicate whether the processor
40  * should be working on the upper half or the lower half of the DMA
41  * data buffers. (The user gets to work on
42  * one half of the data, while the DMAs are streaming the other half.)
43  */
44 volatile int Lower_Ready = 0; // Set by the ISR to indicate which
45  // buffer is available
46 
47 /*
48  * Pointers to the half-buffer which should be worked upon
49  */
50 static volatile uint32_t * inbuf;
51 static volatile uint32_t * outbuf;
52 
53 
54 
55 /*
56  Simple function to return block size.
57  Needed for creating a working buffer.
58 */
60 {
61  return ADC_Block_Size;
62 }
63 
64 /*
65  * Set the number of samples that the user should expect to process per block
66  *
67  */
68 void setblocksize( uint32_t blksiz )
69 {
70  /*
71  * setblocksize() should only be called before calling initialize().
72  * If the ADC & DAC buffers have already been allocated, then initialize()
73  * must have already been called, and we're too late to change the buffer
74  * sizes. Flag an error and return without changing anything.
75  */
76  if (ADC_Input_Buffer != NULL) {
78  return;
79  }
80 
81  ADC_Block_Size = blksiz;
82  ADC_Buffer_Size = 2*blksiz;
83 }
84 
85 /*
86  getblock() is called by the user when the user is
87  ready to process a new block of samples. The user
88  provides a pointer to an (already allocated) buffer
89  that will be filled with float samples.
90 
91  Output samples will range from -1.0 to +1.0 to cover
92  the range of the ADC input voltages. The converter is
93  a 12-bit device, so the resolution of the output samples
94  is roughly 2/4096 = 0.000488.
95 
96  Assuming the original voltage range of the ADC is 0 to 3.0 volts,
97  the conversion between the returned sample value and the input
98  voltage is:
99  Vin = 1.5 + (sample*1.5)
100  (A zero sample value indicates the ADC is mid-scale, at 1.5 volts)
101  For a 0-3 V ADC range, the ADC resolution is 3/4096 = 732 uV.
102 */
103 void getblock(float * working)
104 {
105  uint32_t i;
106 
107  // Wait for the DMA to finish filling a block of data
109  while (Sampler_Status == WAIT_FOR_NEXT_BUFFER) __WFI();
110 
111  // The DMA ISR sets the Lower_Ready flag to indicate whether we should
112  // be processing the upper or lower half of the DMA transfer block.
113  if (Lower_Ready) {
116  } else {
119  }
120 
121  // Now convert the valid ADC data into the caller's array of floats.
122  // Samples are normalized to range from -1.0 to 1.0
123  for (i=0; i< ADC_Block_Size; i++) {
124  // 1/32768 = 3.0517578e-05 (Multiplication is much faster than dividing)
125  working[i] = ((float)((int)inbuf[i]-32767))*3.0517578e-05f;
126  }
127 }
128 
129 /*
130  putblock() is called by the user when they've finished
131  processing a block and are ready to send samples to
132  the DAC for output.
133 
134  Samples are converted from type float to 16-bit samples
135  for the DAC. The result are placed in the DAC DMA buffer.
136 
137  Input sample values should range from -1.0 to 1.0 to cover
138  the full-scale range of the DAC
139 
140  (The DAC is actually a 12-bit converter, with the data
141  aligned in the upper portion of the 16-bit output value)
142 */
143 void putblock(float * working)
144 {
145  uint32_t i;
146 
147  // the "outbuf" pointer is set by getblock() to indicate the
148  // appropriate destination of any output samples.
149  //
150  // floating point values between -1 and +1 are mapped
151  // into the range of the DAC
152  for (i=0; i<ADC_Block_Size; i++) {
153  outbuf[i] = ((int)((working[i]+1.0)*32768.0f)) & 0x0000ffff;
154  }
155 }
156 
157 
158 void putblockstereo(float * chan1, float * chan2)
159 {
160  uint32_t i;
161 
163  putblock( chan1 );
164  return;
165  }
166 
167  // the "outbuf" pointer is set by getblock() to indicate the
168  // appropriate destination of any output samples.
169  //
170  // floating point values between -1 and +1 are mapped
171  // into the range of the DAC
172  //
173  // chan1 goes into the most-significant 16 bits (DAC1),
174  // chan2 in the least significant (DAC1)
175  for (i=0; i<ADC_Block_Size; i++) {
176  outbuf[i] = ( ((int)((chan2[i]+1.0)*32768.0f)) & 0x0000ffff ) |
177  ((((int)((chan1[i]+1.0)*32768.0f)) & 0x0000ffff)<<16);
178  }
179 }
180 
181 
182 void getblockstereo(float *chan1, float *chan2)
183 {
184  uint32_t i;
185 
186  if (Input_Configuration == MONO_IN) {
187  getblock( chan1 );
188  return;
189  }
190 
191  // Wait for the DMA to finish filling a block of data
193  while (Sampler_Status == WAIT_FOR_NEXT_BUFFER) __WFI();
194 
195  // The DMA ISR sets the Lower_Ready flag to indicate whether we should
196  // be processing the upper or lower half of the DMA transfer block.
197  if (Lower_Ready) {
200  } else {
203  }
204 
205  // Now convert the valid ADC data into the caller's arrays of floats.
206  // Samples are normalized to range from -1.0 to 1.0
207  // Channel 1 is in the least significant 16 bits of the DMA transfer data,
208  // Channel 2 is in the most significant 16 bits.
209  for (i=0; i< ADC_Block_Size; i++) {
210  // 1/32768 = 3.0517578e-05 (Multiplication is much faster than dividing)
211  chan1[i]=((float)( (int)(inbuf[i]&0x0000ffff)-32767))*3.0517578e-05f;
212  chan2[i]=((float)( (int)((inbuf[i]&0xffff0000)>>16)-32767))*3.0517578e-05f;
213  }
214 }
215 
216 
217 /*
218  * Interrupt Callback Routines
219  */
220 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
221 {
222  // The ADC has filled the input buffer, and is beginning to over-write
223  // the beginning of the array. The user should get to work processing the
224  // end of the array.
225  Lower_Ready = 0;
226 
227  if (Sampler_Status == STARTUP) {
228  // No need to do anything... user is still initializing
229  }
230  else if (Sampler_Status == WAIT_FOR_NEXT_BUFFER) {
231  Sampler_Status = PROCESS_BUFFER; // Turn the supervisor loose on the
232  // next buffer
233  } else {
234  flagerror(SAMPLE_OVERRUN); // If the supervisor was not waiting for the next
235  // buffer, flag the error to let him/her know that
236  // they're missing blocks of data.
237  }
238 
239 }
240 
241 void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
242 {
243  // The ADC has half-filled the input buffer, and is beginning to over-write
244  // the second-half of the array. The user should get to work processing the
245  // beginning of the array.
246  Lower_Ready = 1;
247 
248  if (Sampler_Status == STARTUP) {
249  // No need to do anything... user is still initializing
250  }
251  else if (Sampler_Status == WAIT_FOR_NEXT_BUFFER) {
252  Sampler_Status = PROCESS_BUFFER; // Turn the supervisor loose on the
253  // next buffer
254  } else {
255  flagerror(SAMPLE_OVERRUN); // If the supervisor was not waiting for the next
256  // buffer, flag the error to let him/her know that
257  // they're missing blocks of data.
258  }
259 }
260 
volatile uint32_t * ADC_Input_Buffer
Buffer to store samples transfered from the ADC by a DMA.
Definition: sample486.c:21
static volatile uint32_t * inbuf
Definition: sample486.c:50
volatile int Lower_Ready
Flag to indicate which half of ADC buffer may be processed.
Definition: sample486.c:44
Mono Input: Only configure ADC1, single DMA Transfer.
Definition: init486.h:145
static volatile uint32_t * outbuf
Definition: sample486.c:51
enum Processor_Task volatile Sampler_Status
Definition: sample486.c:36
User is working on a buffer of data.
Definition: sample486.h:64
void getblockstereo(float *chan1, float *chan2)
Request a block of data from the two ADC sample streams (Stereo input case)
Definition: sample486.c:182
volatile uint32_t * DAC_Output_Buffer
Buffer to hold samples to be transfered to the DAC(s) by a DMA.
Definition: sample486.c:22
void setblocksize(uint32_t blksiz)
Set the number of samples that the user should expect to process per block.
Definition: sample486.c:68
uint32_t ADC_Buffer_Size
Total buffer size being filled by DMA for ADC/DAC.
Definition: sample486.c:28
void flagerror(int errorcode)
Records and indicates an error condition.
Definition: err486.c:48
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
Definition: sample486.c:241
void getblock(float *working)
Request a block of data from the ADC sample stream.
Definition: sample486.c:103
#define SETBLOCKSIZE_ERROR
setblocksize() must be called BEFORE initialize()
Definition: err486.h:61
User is done working... waiting for the next buffer.
Definition: sample486.h:65
enum Num_Channels_Out Output_Configuration
Definition: init486.c:87
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
Definition: sample486.c:220
void putblock(float *working)
Send a buffer of data to the DAC output stream.
Definition: sample486.c:143
ECE 486 Interface fuctions to manipulate blocks of sampled data.
Error Handling for ECE 486 STM32L476G-Discovery Interface.
Processor_Task
keep track of whether a user is working on a buffer, or waiting for the next buffer ...
Definition: sample486.h:62
int getblocksize()
Return the number of samples that the user should expect to process per block.
Definition: sample486.c:59
Mono Output: Only configure DAC1, single DMA Transfer.
Definition: init486.h:154
uint32_t ADC_Block_Size
Number of samples user accesses per data block.
Definition: sample486.c:27
void putblockstereo(float *chan1, float *chan2)
Send two buffers of data to the DACs for stereo output.
Definition: sample486.c:158
User is not ready for data yet.
Definition: sample486.h:63
enum Num_Channels_In Input_Configuration
Definition: init486.c:88
#define DEFAULT_BLOCKSIZE
Default # samples per block of streamed ADC/DAC data.
Definition: sample486.h:23
#define SAMPLE_OVERRUN
ADC buffer filled before the user serviced the buffer.
Definition: err486.h:57