Browse code

Add source code of the firmware for the IO Board

Pawel Jablonski authored on 05/06/2023 17:01:27
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,322 @@
1
+/*
2
+ * Author: Pawel Jablonski
3
+ * E-mail: pj@xirx.net
4
+ * WWW: xirx.net
5
+ * GIT: git.xirx.net
6
+ *
7
+ * License: You can use this code however you like
8
+ * but leave information about the original author.
9
+ * Code is free for non-commercial and commercial use.
10
+ */
11
+
12
+#include <stm32f0xx.h>
13
+#include <stm32f0xx_hal.h>
14
+#include <stm32f0xx_hal_gpio.h>
15
+#include <stm32f0xx_hal_dac.h>
16
+#include <stm32f0xx_hal_tim.h>
17
+#include <stm32f0xx_hal_dma.h>
18
+#include <stm32f0xx_hal_rcc.h>
19
+#include <system_stm32f0xx.h>
20
+
21
+#include "type.h"
22
+
23
+#include "speaker.h"
24
+
25
+#define SPEAKER_BUFFER_SIZE 32 //!< Note buffer size
26
+#define SPEAKER_BUFFER_OUT_SIZE 16 //!< Output buffer size and square wave fill divider
27
+
28
+#define SPEAKER_NOTE_QUANTITY 89 //!< Note quantity available to play
29
+
30
+#define SPEAKER_TIME_MIN 1 //!< Minimum time to play for a note in ms multiplied by TIME_MUL
31
+#define SPEAKER_TIME_MAX 250 //!< Maximum time to play for a note in ms multiplied by TIME_MUL
32
+#define SPEAKER_TIME_MUL 10 //!< Time multiplicator for a note
33
+
34
+#define SPEAKER_FILL_MIN 1 //!< Minimum fill of the square wave in percents divided by SPEAKER_BUFFER_OUT_SIZE
35
+#define SPEAKER_FILL_MAX 15 //!< Maximum fill of the square wave in percents divided by SPEAKER_BUFFER_OUT_SIZE
36
+
37
+#define SPEAKER_VOLUME_MAX 15.0 //!< Maximum volume level
38
+
39
+#define SPEAKER_VALUE_ZERO 2048 //!< Output zero level for silence
40
+#define SPEAKER_VALUE_MUL 2047.0 //!< Output level multiplicator used to conversion
41
+#define SPEAKER_VALUE_VOLUME 0.1 //!< Output volume level multiplicator
42
+
43
+//! List of frequencies for notes
44
+static const unsigned short noteFrequency[SPEAKER_NOTE_QUANTITY] = {
45
+	20, // Silent
46
+	27, 29, 31, //  A0 - B0
47
+	33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, // C1 - B1
48
+	65, 69, 73, 78, 82, 87, 93, 98, 104, 110, 117, 123, // C2 - B2
49
+	131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, // C3 - B3
50
+	262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, // C4 - B4
51
+	523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, // C5 - B5
52
+	1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, // C6 - B6
53
+	2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, // C7 - B7
54
+	4186 // C8
55
+};
56
+
57
+static volatile bool isPlaying = FALSE; //!< Playing status
58
+
59
+static volatile unsigned int bufferStart = 0; //!< Data start position
60
+static volatile unsigned int bufferEnd = 0; //!< Data end position
61
+
62
+static DAC_HandleTypeDef dacHandle; //!< DAC HAL handle
63
+static TIM_HandleTypeDef timHandle; //!< Timer HAL handle for playing square wave
64
+static DAC_ChannelConfTypeDef channelConf; //!< Configuration of DAC HAL
65
+static DMA_HandleTypeDef dmaHandle; //!< DMA HAL handle
66
+static TIM_HandleTypeDef timNoteHandle; //!< Timer HAL handle for making a delay between notes
67
+
68
+static struct SpeakerNote buffer[SPEAKER_BUFFER_SIZE]; //!< Circular note buffer
69
+
70
+static uint16_t bufferOut[SPEAKER_BUFFER_OUT_SIZE]; //!< Output buffer
71
+
72
+static void speakerPlay(unsigned int offset);
73
+
74
+//! Init speaker and set up DMA, DAC, Timers
75
+void speakerInit(void)
76
+{
77
+	for(int i = 0; i < SPEAKER_BUFFER_OUT_SIZE; i++)
78
+	{
79
+		bufferOut[i] = SPEAKER_VALUE_ZERO;
80
+	}
81
+	
82
+	__HAL_RCC_GPIOA_CLK_ENABLE();
83
+	
84
+	GPIO_InitTypeDef gpioInit = {0};
85
+	
86
+	gpioInit.Pin = GPIO_PIN_4;
87
+	gpioInit.Mode = GPIO_MODE_ANALOG;
88
+	gpioInit.Pull = GPIO_NOPULL;
89
+	gpioInit.Speed = GPIO_SPEED_FREQ_LOW;
90
+	
91
+	HAL_GPIO_Init(GPIOA, &gpioInit);
92
+	
93
+	__HAL_RCC_TIM2_CLK_ENABLE();
94
+	
95
+	timHandle.Instance = TIM2;
96
+	timHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
97
+	timHandle.Init.Prescaler = 0;
98
+	timHandle.Init.Period = ((HAL_RCC_GetHCLKFreq() / 100000) - 1);
99
+	timHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
100
+	timHandle.Init.RepetitionCounter = 0;
101
+	
102
+	HAL_TIM_Base_Init(&timHandle);
103
+	HAL_TIM_Base_Start_IT(&timHandle);
104
+	
105
+	TIM_MasterConfigTypeDef sMasterConfig = {0};
106
+
107
+	sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
108
+	sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
109
+	HAL_TIMEx_MasterConfigSynchronization(&timHandle, &sMasterConfig);
110
+	
111
+	__HAL_RCC_DAC1_CLK_ENABLE();
112
+	
113
+	dacHandle.Instance = DAC;
114
+	
115
+	HAL_DAC_Init(&dacHandle);
116
+	
117
+	channelConf.DAC_Trigger = DAC_TRIGGER_T2_TRGO;
118
+    channelConf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
119
+	
120
+	HAL_DAC_ConfigChannel(&dacHandle, &channelConf, DAC_CHANNEL_1);
121
+	HAL_DAC_Start(&dacHandle, DAC_CHANNEL_1);
122
+	
123
+	__HAL_RCC_DMA1_CLK_ENABLE();
124
+	
125
+	dmaHandle.Instance = DMA1_Channel3;
126
+	dmaHandle.Init.Direction = DMA_MEMORY_TO_PERIPH;
127
+	dmaHandle.Init.PeriphInc = DMA_PINC_DISABLE;
128
+	dmaHandle.Init.MemInc = DMA_MINC_ENABLE;
129
+	dmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
130
+	dmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
131
+	dmaHandle.Init.Mode = DMA_CIRCULAR;
132
+	dmaHandle.Init.Priority = DMA_PRIORITY_HIGH;
133
+	
134
+	HAL_DMA_Init(&dmaHandle);
135
+	
136
+	__HAL_LINKDMA(&dacHandle, DMA_Handle1, dmaHandle);
137
+	
138
+	HAL_DAC_Start_DMA(&dacHandle, DAC_CHANNEL_1, (uint32_t *)bufferOut, SPEAKER_BUFFER_OUT_SIZE, DAC_ALIGN_12B_R);
139
+	
140
+	__HAL_RCC_TIM6_CLK_ENABLE();
141
+	
142
+	timNoteHandle.Instance = TIM6;
143
+	timNoteHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
144
+	timNoteHandle.Init.Prescaler = ((HAL_RCC_GetHCLKFreq() / 1000) - 1);
145
+	timNoteHandle.Init.Period = 0;
146
+	timNoteHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
147
+	timNoteHandle.Init.RepetitionCounter = 0;
148
+	
149
+	HAL_TIM_Base_Init(&timNoteHandle);
150
+	
151
+	HAL_NVIC_SetPriority(TIM6_IRQn, 3, 0);
152
+	HAL_NVIC_EnableIRQ(TIM6_IRQn);
153
+}
154
+
155
+//! Clear the speaker buffers and stop playing sound
156
+void speakerClear(void)
157
+{
158
+	__disable_irq();
159
+	{
160
+		isPlaying = FALSE;
161
+		
162
+		bufferStart = 0;
163
+		bufferEnd = 0;
164
+		
165
+		HAL_TIM_Base_Stop_IT(&timNoteHandle);
166
+	
167
+		for(int i = 0; i < SPEAKER_BUFFER_OUT_SIZE; i++)
168
+		{
169
+			bufferOut[i] = SPEAKER_VALUE_ZERO;
170
+		}
171
+	}
172
+	__enable_irq();
173
+}
174
+
175
+/**
176
+ * Add note to the buffer
177
+ *
178
+ * @param note Note to add to the buffer
179
+ */
180
+void speakerAddNote(struct SpeakerNote note)
181
+{
182
+	if(note.note >= SPEAKER_NOTE_QUANTITY)
183
+	{
184
+		return;
185
+	}
186
+	
187
+	if((note.time < SPEAKER_TIME_MIN) || (note.time > SPEAKER_TIME_MAX))
188
+	{
189
+		return;
190
+	}
191
+	
192
+	if(note.note != NOTE_SILENT)
193
+	{
194
+		if((note.fill < SPEAKER_FILL_MIN) || (note.fill > SPEAKER_FILL_MAX))
195
+		{
196
+			return;
197
+		}
198
+		
199
+		if(note.volume > SPEAKER_VOLUME_MAX)
200
+		{
201
+			return;
202
+		}
203
+	}
204
+	
205
+	__disable_irq();
206
+	{
207
+		unsigned int last = bufferEnd;
208
+		
209
+		if(bufferStart == ((last + 1) % SPEAKER_BUFFER_SIZE))
210
+		{
211
+			__enable_irq();
212
+			return;
213
+		}
214
+		
215
+		buffer[last].note = note.note;
216
+		buffer[last].time = note.time;
217
+		
218
+		if(note.note == NOTE_SILENT)
219
+		{
220
+			buffer[last].fill = SPEAKER_FILL_MIN;
221
+			buffer[last].volume = 0;
222
+		}
223
+		else
224
+		{
225
+			buffer[last].fill = note.fill;
226
+			buffer[last].volume = note.volume;
227
+		}
228
+		
229
+		bufferEnd = ((last + 1) % SPEAKER_BUFFER_SIZE);
230
+
231
+		if(!isPlaying)
232
+		{
233
+			__enable_irq();
234
+			
235
+			speakerPlay(bufferStart);
236
+			
237
+			return;
238
+		}
239
+	}
240
+	__enable_irq();
241
+	
242
+	return;
243
+}
244
+
245
+/**
246
+ * Get free space size in the buffer
247
+ *
248
+ * @return Free space size in the buffer
249
+ */
250
+unsigned char speakerGetBufferFree(void)
251
+{
252
+	unsigned char bufferFree = (SPEAKER_BUFFER_SIZE - 1);
253
+	
254
+	__disable_irq();
255
+	{
256
+		if(bufferStart < bufferEnd)
257
+		{
258
+			bufferFree -= (bufferEnd - bufferStart);
259
+		}
260
+		else if(bufferStart > bufferEnd)
261
+		{
262
+			bufferStart = ((bufferStart - bufferEnd) - 1);
263
+		}	
264
+	}
265
+	__enable_irq();
266
+	
267
+	return(bufferFree);
268
+}
269
+
270
+/**
271
+ * Play a note
272
+ *
273
+ * @param offset Position of the note in the note buffer
274
+ */
275
+static void speakerPlay(unsigned int offset)
276
+{
277
+	__disable_irq();
278
+	{
279
+		uint16_t signalLevel = (uint16_t)(SPEAKER_VALUE_MUL * SPEAKER_VALUE_VOLUME * ((float)(buffer[offset].volume) / SPEAKER_VOLUME_MAX));
280
+		
281
+		uint16_t high = (SPEAKER_VALUE_ZERO + signalLevel);
282
+		uint16_t low = (SPEAKER_VALUE_ZERO - signalLevel);
283
+		
284
+		for(int i = 0; i < SPEAKER_BUFFER_OUT_SIZE; i++)
285
+		{
286
+			bufferOut[i] = ((i < buffer[offset].fill) ? high : low);
287
+		}
288
+		
289
+		__HAL_TIM_SET_AUTORELOAD(&timHandle, ((HAL_RCC_GetHCLKFreq() / (noteFrequency[buffer[offset].note] * SPEAKER_BUFFER_OUT_SIZE)) - 1));
290
+		__HAL_TIM_SET_COUNTER(&timHandle, 0);
291
+		
292
+		__HAL_TIM_SET_AUTORELOAD(&timNoteHandle, (((uint32_t)(buffer[offset].time) * SPEAKER_TIME_MUL) - 1));
293
+		__HAL_TIM_SET_COUNTER(&timNoteHandle, 0);
294
+		
295
+		if(!isPlaying)
296
+		{
297
+			isPlaying = TRUE;
298
+			
299
+			HAL_TIM_Base_Start_IT(&timNoteHandle);
300
+		}
301
+	}
302
+	__enable_irq();
303
+}
304
+
305
+//! Load and play a next note from the note buffer. It is an IRQ handler connected to the timer HAL handle for making a delay between notes.
306
+void TIM6_IRQHandler(void)
307
+{
308
+	if(bufferStart == bufferEnd)
309
+	{
310
+		speakerClear();
311
+	}
312
+	else
313
+	{
314
+		unsigned int first = bufferStart;
315
+
316
+		speakerPlay(first);
317
+		
318
+		bufferStart = ((first + 1) % SPEAKER_BUFFER_SIZE);
319
+	}
320
+	
321
+	HAL_TIM_IRQHandler(&timNoteHandle);	
322
+}