semf
stm32spimaster.cpp
Go to the documentation of this file.
1
12#include <algorithm>
13#include <array>
14
15#if defined(STM32) && defined(HAL_SPI_MODULE_ENABLED)
16namespace semf
17{
18Stm32SpiMaster::Stm32SpiMaster(SPI_HandleTypeDef& hwHandle, uint32_t spiClockFrequencyHz)
19: m_hwHandle(&hwHandle),
20 m_spiClockFrequencyHz(spiClockFrequencyHz)
21{
22 queue()->push(*this);
23 aborted.connect(m_abortedSlot);
24}
25
26Stm32SpiMaster::Stm32SpiMaster(SPI_HandleTypeDef& hwHandle, Gpio& chipSelectPin, uint32_t spiClockFrequencyHz)
27: SpiMasterHardware(chipSelectPin),
28 m_hwHandle(&hwHandle),
29 m_spiClockFrequencyHz(spiClockFrequencyHz)
30{
31 queue()->push(*this);
32 aborted.connect(m_abortedSlot);
33}
34
36{
37 SEMF_INFO("init");
38 HAL_StatusTypeDef state = HAL_SPI_Init(m_hwHandle);
39 if (state != HAL_OK)
40 {
41 if (state == HAL_ERROR)
42 {
43 SEMF_ERROR("hal error");
44 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::Init_HalError)));
45 }
46 else if (state == HAL_BUSY)
47 {
48 SEMF_ERROR("hal busy");
49 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::Init_HalBusy)));
50 }
51 else if (state == HAL_TIMEOUT)
52 {
53 SEMF_ERROR("hal timeout");
54 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::Init_HalTimeout)));
55 }
56 return;
57 }
58}
59
61{
62 SEMF_INFO("deinit");
63 HAL_StatusTypeDef state = HAL_SPI_DeInit(m_hwHandle);
64 if (state != HAL_OK)
65 {
66 if (state == HAL_ERROR)
67 {
68 SEMF_ERROR("hal error");
69 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::Init_HalError)));
70 }
71 else if (state == HAL_BUSY)
72 {
73 SEMF_ERROR("hal busy");
74 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::Init_HalError)));
75 }
76 else if (state == HAL_TIMEOUT)
77 {
78 SEMF_ERROR("hal timeout");
79 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::Init_HalError)));
80 }
81 return;
82 }
83}
84
86{
88 {
89 SEMF_ERROR("is busy");
90 error(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::SetFrequency_IsBusy)));
91 }
92
93 if (m_spiClockFrequencyHz == 0)
94 {
95 SEMF_ERROR("Hz is zero");
96 error(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::SetFrequency_SpiClockIsZero)));
97 }
98
99 deinit();
100 std::array<uint32_t, 8> frequencies;
101
102 uint8_t prescaler = 1;
103
104 std::generate(frequencies.begin(), frequencies.end(),
105 [this, prescaler]() mutable
106 {
107 uint32_t freq = m_spiClockFrequencyHz / (prescaler + 1);
108 prescaler = static_cast<uint8_t>(prescaler | (prescaler << 1));
109 return freq;
110 });
111
112 auto upperIt = std::upper_bound(frequencies.rbegin(), frequencies.rend(), hz);
113 auto lowerIt = std::lower_bound(frequencies.rbegin(), frequencies.rend(), hz);
114
115 if (*lowerIt != hz)
116 lowerIt--;
117
118 uint32_t upper = upperIt.base() == frequencies.end() ? frequencies.back() : *upperIt;
119 uint32_t lower = lowerIt == frequencies.rend() ? frequencies.front() : *lowerIt;
120
121 uint32_t best = upper - hz < hz - lower ? upper : lower;
122
123 SEMF_INFO("closest possible frequency is %d Hz", best);
124
125 auto bestIt = std::find(frequencies.begin(), frequencies.end(), best);
126
127 if (bestIt - frequencies.begin() == 0)
128 m_hwHandle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
129 else if (bestIt - frequencies.begin() == 1)
130 m_hwHandle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
131 else if (bestIt - frequencies.begin() == 2)
132 m_hwHandle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
133 else if (bestIt - frequencies.begin() == 3)
134 m_hwHandle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
135 else if (bestIt - frequencies.begin() == 4)
136 m_hwHandle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
137 else if (bestIt - frequencies.begin() == 5)
138 m_hwHandle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
139 else if (bestIt - frequencies.begin() == 6)
140 m_hwHandle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
141 else if (bestIt - frequencies.begin() == 7)
142 m_hwHandle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
143
144 init();
145}
146
148{
150 return &queue;
151}
152
153void Stm32SpiMaster::systemIsrRead(SPI_HandleTypeDef& spi)
154{
155 for (Stm32SpiMaster& i : *(queue()))
156 i.isrRead(spi);
157}
158
159void Stm32SpiMaster::systemIsrWritten(SPI_HandleTypeDef& spi)
160{
161 for (Stm32SpiMaster& i : *(queue()))
162 i.isrWritten(spi);
163}
164
165void Stm32SpiMaster::systemIsrWrittenAndRead(SPI_HandleTypeDef& spi)
166{
167 for (Stm32SpiMaster& i : *(queue()))
168 i.isrWrittenAndRead(spi);
169}
170
171void Stm32SpiMaster::systemIsrError(SPI_HandleTypeDef& spi)
172{
173 for (Stm32SpiMaster& i : *(queue()))
174 i.isrError(spi);
175}
176
177void Stm32SpiMaster::systemIsrAbort(SPI_HandleTypeDef& spi)
178{
179 for (Stm32SpiMaster& i : *(queue()))
180 i.isrAborted(spi);
181}
182
183void Stm32SpiMaster::isrRead(SPI_HandleTypeDef& spi)
184{
185 if (&spi != m_hwHandle)
186 return;
187
188 m_dataIndex++;
189
190 // Problem in HAL_SPI_Receive, so we always do one byte after the other
191 if (m_dataIndex < m_dataSize)
192 {
193 readNext();
194 }
195 else
196 {
200 }
201}
202
203void Stm32SpiMaster::isrWritten(SPI_HandleTypeDef& spi)
204{
205 if (&spi != m_hwHandle)
206 return;
207
210
212}
213
214void Stm32SpiMaster::isrWrittenAndRead(SPI_HandleTypeDef& spi)
215{
216 if (&spi != m_hwHandle)
217 return;
218
219 m_dataIndex++;
220
221 // Problem in HAL_SPI_Transmit, so we always do one byte after the other
222 if (m_dataIndex < m_dataSize)
223 {
224 writeReadNext();
225 }
226 else // Finished
227 {
230
232 }
233}
234
235void Stm32SpiMaster::isrError(SPI_HandleTypeDef& spi)
236{
237 if (&spi != m_hwHandle)
238 return;
239
240 SEMF_INFO("isr error");
241 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::IsrError_Isr)));
242}
243
244void Stm32SpiMaster::isrAborted(SPI_HandleTypeDef& spi)
245{
246 if (&spi != m_hwHandle)
247 return;
248
249 SEMF_INFO("isr abort");
250 setBusy(false);
251 aborted();
252}
253
254void Stm32SpiMaster::writeHardware(const uint8_t data[], size_t dataSize)
255{
256 SEMF_INFO("data %p, size %u", data, dataSize);
257 __HAL_UNLOCK(m_hwHandle);
258 HAL_StatusTypeDef state = HAL_SPI_Transmit_IT(m_hwHandle, const_cast<uint8_t*>(data), static_cast<uint16_t>(dataSize));
259 if (state != HAL_OK)
260 {
261 if (state == HAL_ERROR)
262 {
263 SEMF_ERROR("hal error");
264 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::Write_HalError)));
265 }
266 else if (state == HAL_BUSY)
267 {
268 SEMF_ERROR("hal busy");
269 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::Write_HalBusy)));
270 }
271 else if (state == HAL_TIMEOUT)
272 {
273 SEMF_ERROR("hal timeout");
274 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::Write_HalTimeout)));
275 }
276 return;
277 }
278}
279
280void Stm32SpiMaster::readHardware(uint8_t buffer[], size_t bufferSize)
281{
282 SEMF_INFO("data %p, size %u", buffer, bufferSize);
283 m_readData = const_cast<uint8_t*>(buffer);
284 m_dataSize = bufferSize;
285 m_dataIndex = 0;
286 readNext();
287}
288
289void Stm32SpiMaster::writeReadHardware(const uint8_t writeData[], uint8_t readBuffer[], size_t size)
290{
291 SEMF_INFO("write data %p, read data %p, size %u", writeData, readBuffer, size);
292 m_writeData = writeData;
293 m_readData = readBuffer;
294 m_dataSize = size;
295 m_dataIndex = 0;
296 writeReadNext();
297}
298
300{
301 if (bits == 8)
302 m_hwHandle->Init.DataSize = SPI_DATASIZE_8BIT;
303 else if (bits == 16)
304 m_hwHandle->Init.DataSize = SPI_DATASIZE_16BIT;
305 else
306 {
307 SEMF_ERROR("%d bits not supported");
308 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::SetFormatHardware_BitsNotSupported)));
309 return;
310 }
311
312 switch (transmission)
313 {
315 m_hwHandle->Init.CLKPolarity = SPI_POLARITY_LOW;
316 m_hwHandle->Init.CLKPhase = SPI_PHASE_1EDGE;
317 break;
319 m_hwHandle->Init.CLKPolarity = SPI_POLARITY_LOW;
320 m_hwHandle->Init.CLKPhase = SPI_PHASE_2EDGE;
321 break;
323 m_hwHandle->Init.CLKPolarity = SPI_POLARITY_HIGH;
324 m_hwHandle->Init.CLKPhase = SPI_PHASE_1EDGE;
325 break;
327 m_hwHandle->Init.CLKPolarity = SPI_POLARITY_HIGH;
328 m_hwHandle->Init.CLKPhase = SPI_PHASE_2EDGE;
329 break;
330 }
331
332 switch (wire)
333 {
336 m_hwHandle->Init.Direction = SPI_DIRECTION_2LINES;
337 break;
339 m_hwHandle->Init.Direction = SPI_DIRECTION_1LINE;
340 break;
342 m_hwHandle->Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
343 break;
344 }
345}
346
348{
349 m_abortedSlot.setObject(writeStopped);
350 auto state = HAL_SPI_Abort_IT(m_hwHandle);
351 if (state != HAL_OK)
352 {
353 if (state == HAL_ERROR)
354 {
355 SEMF_ERROR("hal error");
356 error(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::StopWriteHardware_HalError)));
357 }
358 else if (state == HAL_BUSY)
359 {
360 SEMF_ERROR("hal busy");
361 error(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::StopWriteHardware_HalBusy)));
362 }
363 else if (state == HAL_TIMEOUT)
364 {
365 SEMF_ERROR("hal timeout");
366 error(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::StopWriteHardware_HalTimeout)));
367 }
368 return;
369 }
370}
371
373{
374 m_abortedSlot.setObject(readStopped);
375 auto state = HAL_SPI_Abort_IT(m_hwHandle);
376 if (state != HAL_OK)
377 {
378 if (state == HAL_ERROR)
379 {
380 SEMF_ERROR("hal error");
381 error(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::StopReadHardware_HalError)));
382 }
383 else if (state == HAL_BUSY)
384 {
385 SEMF_ERROR("hal busy");
386 error(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::StopReadHardware_HalBusy)));
387 }
388 else if (state == HAL_TIMEOUT)
389 {
390 SEMF_ERROR("hal timeout");
391 error(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::StopReadHardware_HalTimeout)));
392 }
393 return;
394 }
395}
396
397void Stm32SpiMaster::readNext()
398{
399 SEMF_INFO("read next");
400 __HAL_UNLOCK(m_hwHandle);
401 HAL_StatusTypeDef state = HAL_SPI_Receive_IT(m_hwHandle, &m_readData[m_dataIndex], 1);
402 if (state != HAL_OK)
403 {
404 if (state == HAL_ERROR)
405 {
406 SEMF_ERROR("hal error");
407 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::ReadNext_HalError)));
408 }
409 else if (state == HAL_BUSY)
410 {
411 SEMF_ERROR("hal busy");
412 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::ReadNext_HalBusy)));
413 }
414 else if (state == HAL_TIMEOUT)
415 {
416 SEMF_ERROR("hal timeout");
417 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::ReadNext_HalTimeout)));
418 }
419 return;
420 }
421}
422
423void Stm32SpiMaster::writeReadNext()
424{
425 SEMF_INFO("write / read next");
426 __HAL_UNLOCK(m_hwHandle);
427 HAL_StatusTypeDef state = HAL_SPI_TransmitReceive_IT(m_hwHandle, const_cast<uint8_t*>(&m_writeData[m_dataIndex]), &m_readData[m_dataIndex], 1);
428 if (state != HAL_OK)
429 {
430 if (state == HAL_ERROR)
431 {
432 SEMF_ERROR("hal error");
433 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::WriteReadNext_HalError)));
434 }
435 else if (state == HAL_BUSY)
436 {
437 SEMF_ERROR("hal busy");
438 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::WriteReadNext_HalError)));
439 }
440 else if (state == HAL_TIMEOUT)
441 {
442 SEMF_ERROR("hal timeout");
443 onError(Error(kSemfClassId, static_cast<uint8_t>(ErrorCode::WriteReadNext_HalError)));
444 }
445 return;
446 }
447}
448} /* namespace semf */
449#endif
@ FirstAndLast
start AND stop condition
Class for representing errors. Every error should have a unique source code. As a user feel encourage...
Definition: error.h:22
Interface class for using a GPIO pin of the microcontroller.
Definition: gpio.h:23
LinkedQueue is an managed single linked queue implementation.
Definition: linkedqueue.h:38
void connect(SlotBase< Arguments... > &slot)
Connect a method to the signal.
Definition: signal.h:94
WireMode
Definition: spi.h:37
@ WriteOnlyWires
CLK and MOSI pins used (no master receive)
@ ReadOnlyWires
CLK and MISO pins used (no master write)
@ FullDuplexWires
CLK, MISO and MOSI pins used.
@ HalfDuplexWires
CLK, MISO / MOSI pins used (MISO and MOSI pin is shared)
TransmissionMode
Definition: spi.h:29
@ Mode2
POL is high, PHA is low.
@ Mode0
POL is low, PHA is low.
@ Mode1
POL is low, PHA is high.
@ Mode3
POL is high, PHA is high.
Class for using SPI hardware in master mode.
void onError(Error thrown)
Is called if an error occurred by hardware read or write access. Will throw error signal.
CommunicationHardware::Frame frame() const
Returns the actual frame mode setting.
bool isBusyWriting() const override
Communication hardware is busy writing at the moment.
void setBusy(bool isBusy)
Sets the busy flag.
bool isBusyReading() const override
Communication hardware is busy reading at the moment.
SpiMasterHardware implementation for STM32.
void setFrequency(uint32_t hz) override
Sets the speed.
static void systemIsrWrittenAndRead(SPI_HandleTypeDef &spi)
System-wide interrupt service routine for spi receive and transmit.
void isrAborted(SPI_HandleTypeDef &spi)
handles the abort callback
void isrWrittenAndRead(SPI_HandleTypeDef &spi)
Interrupt service routine for spi object receive and transmit.
Stm32SpiMaster(SPI_HandleTypeDef &hwHandle, uint32_t spiClockFrequencyHz=0)
Constructor.
void setFormatHardware(uint8_t bits, TransmissionMode transmission, WireMode wire) override
Configures the hardware for insuring the given configuration.
void writeReadHardware(const uint8_t writeData[], uint8_t readBuffer[], size_t size) override
Hardware will read and write data parallel.
void writeHardware(const uint8_t data[], size_t dataSize) override
Hardware will write data.
static void systemIsrError(SPI_HandleTypeDef &spi)
System-wide interrupt service routine for spi error.
void isrRead(SPI_HandleTypeDef &spi)
Interrupt service routine for spi object receive.
static void systemIsrWritten(SPI_HandleTypeDef &spi)
System-wide interrupt service routine for spi transmit.
static void systemIsrRead(SPI_HandleTypeDef &spi)
System-wide interrupt service routine for spi receive.
void readHardware(uint8_t buffer[], size_t bufferSize) override
Hardware will read data.
static void systemIsrAbort(SPI_HandleTypeDef &spi)
System-wide interrupt service routine for spi abort.
void isrError(SPI_HandleTypeDef &spi)
Interrupt service routine for spi object error.
void isrWritten(SPI_HandleTypeDef &spi)
void init() override
static LinkedQueue< Stm32SpiMaster > * queue()
Get the list with all spi.
void stopReadHardware() override
void deinit() override
void stopWriteHardware() override
Signal< Error > error
Definition: communication.h:64
#define SEMF_ERROR(...)
Definition: debug.h:39
#define SEMF_INFO(...)
Definition: debug.h:41