-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.c
211 lines (145 loc) · 5.55 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#include "em_device.h"
#include "em_chip.h"
#include "em_cmu.h"
#include "em_timer.h"
#include "em_adc.h"
#include "em_vdac.h"
#include "arm_math.h"
#define NUM_BUFFERS 4
#define BUFFER_SIZE 256
#define QUEUE_SIZE (NUM_BUFFERS)
// Create buffers
volatile static float32_t buffer[NUM_BUFFERS][BUFFER_SIZE];
// Create queues
volatile static float32_t *adcQueue[QUEUE_SIZE];
volatile static float32_t *processingQueue[QUEUE_SIZE];
volatile static float32_t *dacQueue[QUEUE_SIZE];
// Create pointers to available buffers and available slots in each respective queue
volatile static uint32_t adcQueueHead = QUEUE_SIZE - 1;
volatile static uint32_t adcQueueTail = QUEUE_SIZE - 1;
volatile static uint32_t processingQueueHead = QUEUE_SIZE - 1;
volatile static uint32_t processingQueueTail = QUEUE_SIZE - 1;
volatile static uint32_t dacQueueHead = QUEUE_SIZE - 1;
volatile static uint32_t dacQueueTail = QUEUE_SIZE - 1;
volatile uint32_t adcBufferIndex;
volatile uint32_t dacBufferIndex;
static void setupSamplingTimer(){
// Enable clocks to Timer
CMU_ClockEnable(cmuClock_TIMER0, true);
// Configure the timer
// We want an up-counting timer that goes up to a number that corresponds to 25 usec
// For a 40 MHz clock (no prescaling), that value is 1000
TIMER_Init_TypeDef init = TIMER_INIT_DEFAULT;
// Don't start the timer after initialization yet
init.enable = false;
TIMER_Init(TIMER0, &init);
// Calculate the number of pulses needed for each sampling period
float32_t samplingPeriod = 1 / 40000.0f;
float32_t numPulsesf = samplingPeriod * 40000000.0f;
uint32_t numPulses = (uint32_t)numPulsesf;
// Set counter limit
TIMER_TopSet(TIMER0, numPulses);
// Enable overflow interrupt
NVIC_EnableIRQ(TIMER0_IRQn);
TIMER_IntEnable(TIMER0, TIMER_IF_OF);
}
static void setupADC(){
// Enable clock to ADC
CMU_ClockEnable(cmuClock_ADC0, true);
ADC_Init_TypeDef init = ADC_INIT_DEFAULT;
init.timebase = ADC_TimebaseCalc(0);
init.prescale = ADC_PrescaleCalc(10000000, 0);
ADC_Init(ADC0, &init);
ADC_InitSingle_TypeDef sInit = ADC_INITSINGLE_DEFAULT;
// Setup single channel mode parameters
sInit.reference = adcRefVDD;
sInit.acqTime = adcAcqTime8; // Take 8 ADC clock cycles to capture sample
sInit.posSel= adcPosSelAPORT0XCH0; // ADC Input = Port PI0
sInit.negSel = adcNegSelVSS; // Single-ended ADC input
sInit.rep = false; // Disable repeated mode
ADC_InitSingle(ADC0, &sInit);
NVIC_EnableIRQ(ADC0_IRQn);
ADC_IntEnable(ADC0, ADC_IF_SINGLE);
ADC_IntEnable(ADC0, ADC_IF_SINGLEOF);
}
static void setupDAC(){
// Enable VDAC clock
CMU_ClockEnable(cmuClock_VDAC0, true);
VDAC_Init_TypeDef vdac_init = VDAC_INIT_DEFAULT;
VDAC_InitChannel_TypeDef vdac_init_channel = VDAC_INITCHANNEL_DEFAULT;
vdac_init.prescaler = VDAC_PrescaleCalc(1000000, true, 0);
VDAC_Init(VDAC0, &vdac_init);
vdac_init_channel.enable = true;
VDAC_InitChannel(VDAC0, &vdac_init_channel, 0);
}
void transferBufferToQueue(volatile float32_t *outgoingBuffer, volatile float32_t **ingoingQueue, volatile uint32_t *ingoingIndex){
// If the slot pointed to by ingoingIndex is NOT NULL, it means that it is already occupied by a buffer
if (ingoingQueue[*ingoingIndex] == NULL){
ingoingQueue[*ingoingIndex] = outgoingBuffer;
*ingoingIndex = (*ingoingIndex + 1) % QUEUE_SIZE;
}
}
int main(void)
{
/* Chip errata */
CHIP_Init();
setupSamplingTimer();
setupADC();
setupDAC();
// Initialize buffers with all 0s
for (int i = 0; i < NUM_BUFFERS; ++i)
arm_fill_f32(0.0, (float32_t *)buffer[i], BUFFER_SIZE);
// Initialize all slots in each queue to NULL pointers
// Except for the adcQueue. That queue should contain all the empty buffers
for (int i = 0; i < QUEUE_SIZE; ++i){
adcQueue[i] = buffer[i];
processingQueue[i] = NULL;
dacQueue[i] = NULL;
}
adcBufferIndex = 0;
dacBufferIndex = 0;
TIMER_Enable(TIMER0, true);
/* Infinite loop */
while (1){
// Check to make sure there is a buffer available for processing
if (processingQueue[processingQueueHead] != NULL){
// Fancy processing code here
transferBufferToQueue(processingQueue[processingQueueHead], dacQueue, &dacQueueTail);
processingQueue[processingQueueHead] = NULL;
processingQueueHead = (processingQueueHead + 1) % QUEUE_SIZE;
}
}
}
// Your ISR functions MUST be named as such
void TIMER0_IRQHandler(){
// Clear interrupt flags
TIMER_IntClear(TIMER0, TIMER_IFC_OF);
// Kick off ADC sampling
ADC_Start(ADC0, adcStartSingle);
// Check to make sure there is a buffer to consume
if (dacQueue[dacQueueHead] != NULL){
// Write an output sample to DAC
VDAC_Channel0OutputSet(VDAC0, (uint32_t)dacQueue[dacQueueHead][dacBufferIndex]);
dacBufferIndex++;
if (dacBufferIndex >= BUFFER_SIZE){
dacBufferIndex = 0;
transferBufferToQueue(dacQueue[dacQueueHead], adcQueue, &adcQueueTail);
dacQueue[dacQueueHead] = NULL;
dacQueueHead = (dacQueueHead + 1) % QUEUE_SIZE;
}
}
}
// ISR for when ADC finishes sampling
void ADC0_IRQHandler(){
// Check to make sure there is an empty buffer available
if (adcQueue[adcQueueHead] != NULL){
adcQueue[adcQueueHead][adcBufferIndex] = (float32_t)ADC0->SINGLEDATA;
adcBufferIndex++;
if (adcBufferIndex >= BUFFER_SIZE){
adcBufferIndex = 0;
transferBufferToQueue(adcQueue[adcQueueHead], processingQueue, &processingQueueTail);
adcQueue[adcQueueHead] = NULL;
adcQueueHead = (adcQueueHead + 1) % QUEUE_SIZE;
}
}
}