Skyward boardcore
Loading...
Searching...
No Matches
USART.cpp
Go to the documentation of this file.
1/* Copyright (c) 2022 Skyward Experimental Rocketry
2 * Author: Emilio Corigliano
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "drivers/usart/USART.h"
24
25#include <fcntl.h>
26#include <stdio.h>
27#include <utils/Debug.h>
28#include <utils/Numeric.h>
29
30#include <fstream>
31#include <string>
32
33#include "arch/common/drivers/serial.h"
34#include "filesystem/file_access.h"
35#include "kernel/scheduler/scheduler.h"
36#include "miosix.h"
37
39Boardcore::USART* ports[N_USART_PORTS];
40
41#ifdef USART1
45void usart1irqImpl();
46
50void __attribute__((used)) usart1irqImplBoardcore()
51{
52 Boardcore::USART* port_boardcore = ports[0];
53 if (port_boardcore)
54 port_boardcore->IRQhandleInterrupt();
55 else
56 usart1irqImpl();
57}
58
62void __attribute__((naked, used)) USART1_IRQHandler()
63{
64 saveContext();
65 asm volatile("bl _Z22usart1irqImplBoardcorev");
66 restoreContext();
67}
68#endif
69
70#ifdef USART2
74void usart2irqImpl();
75
79void __attribute__((used)) usart2irqImplBoardcore()
80{
81 Boardcore::USART* port_boardcore = ports[1];
82 if (port_boardcore)
83 port_boardcore->IRQhandleInterrupt();
84 else
85 usart2irqImpl();
86}
87
91void __attribute__((naked, used)) USART2_IRQHandler()
92{
93 saveContext();
94 asm volatile("bl _Z22usart2irqImplBoardcorev");
95 restoreContext();
96}
97#endif
98
99#ifdef USART3
103void usart3irqImpl();
104
108void __attribute__((used)) usart3irqImplBoardcore()
109{
110 Boardcore::USART* port_boardcore = ports[2];
111 if (port_boardcore)
112 port_boardcore->IRQhandleInterrupt();
113 else
114 usart3irqImpl();
115}
116
120void __attribute__((naked, used)) USART3_IRQHandler()
121{
122 saveContext();
123 asm volatile("bl _Z22usart3irqImplBoardcorev");
124 restoreContext();
125}
126#endif
127
128#ifdef UART4
132void __attribute__((used)) uart4irqImplBoardcore()
133{
134 Boardcore::USART* port_boardcore = ports[3];
135 if (port_boardcore)
136 port_boardcore->IRQhandleInterrupt();
137}
138
142void __attribute__((naked, used)) UART4_IRQHandler()
143{
144 saveContext();
145 asm volatile("bl _Z21uart4irqImplBoardcorev");
146 restoreContext();
147}
148#endif
149
150#ifdef UART5
154void __attribute__((used)) uart5irqImplBoardcore()
155{
156 Boardcore::USART* port_boardcore = ports[4];
157 if (port_boardcore)
158 port_boardcore->IRQhandleInterrupt();
159}
160
164void __attribute__((naked, used)) UART5_IRQHandler()
165{
166 saveContext();
167 asm volatile("bl _Z21uart5irqImplBoardcorev");
168 restoreContext();
169}
170#endif
171
172#ifdef USART6
176void __attribute__((used)) usart6irqImplBoardcore()
177{
178 Boardcore::USART* port_boardcore = ports[5];
179 if (port_boardcore)
180 port_boardcore->IRQhandleInterrupt();
181}
182
186void __attribute__((naked, used)) USART6_IRQHandler()
187{
188 saveContext();
189 asm volatile("bl _Z22usart6irqImplBoardcorev");
190 restoreContext();
191}
192#endif
193
194#ifdef UART7
198void __attribute__((used)) uart7irqImplBoardcore()
199{
200 Boardcore::USART* port_boardcore = ports[6];
201 if (port_boardcore)
202 port_boardcore->IRQhandleInterrupt();
203}
204
208void __attribute__((naked, used)) UART7_IRQHandler()
209{
210 saveContext();
211 asm volatile("bl _Z21uart7irqImplBoardcorev");
212 restoreContext();
213}
214#endif
215
216#ifdef UART8
220void __attribute__((used)) uart8irqImplBoardcore()
221{
222 Boardcore::USART* port_boardcore = ports[7];
223 if (port_boardcore)
224 port_boardcore->IRQhandleInterrupt();
225}
226
230void __attribute__((naked, used)) UART8_IRQHandler()
231{
232 saveContext();
233 asm volatile("bl _Z21uart8irqImplBoardcorev");
234 restoreContext();
235}
236#endif
237
238namespace Boardcore
239{
240
242 : usart(usart), baudrate(baudrate)
243{
244 // Setting the id of the serial port
245 switch (reinterpret_cast<uint32_t>(usart))
246 {
247#ifdef USART1
248 case USART1_BASE:
249 this->id = 1;
250 irqn = USART1_IRQn;
251 this->serialPortName = std::string("usart1");
252 break;
253#endif
254#ifdef USART2
255 case USART2_BASE:
256 this->id = 2;
257 irqn = USART2_IRQn;
258 this->serialPortName = std::string("usart2");
259 break;
260#endif
261#ifdef USART3
262 case USART3_BASE:
263 this->id = 3;
264 irqn = USART3_IRQn;
265 this->serialPortName = std::string("usart3");
266 break;
267#endif
268#ifdef UART4
269 case UART4_BASE:
270 this->id = 4;
271 irqn = UART4_IRQn;
272 this->serialPortName = std::string("uart4");
273 break;
274#endif
275#ifdef UART5
276 case UART5_BASE:
277 this->id = 5;
278 irqn = UART5_IRQn;
279 this->serialPortName = std::string("uart5");
280 break;
281#endif
282#ifdef USART6
283 case USART6_BASE:
284 this->id = 6;
285 irqn = USART6_IRQn;
286 this->serialPortName = std::string("usart6");
287 break;
288#endif
289#ifdef UART7
290 case UART7_BASE:
291 this->id = 7;
292 irqn = UART7_IRQn;
293 this->serialPortName = std::string("uart7");
294 break;
295#endif
296#ifdef UART8
297 case UART8_BASE:
298 this->id = 8;
299 irqn = UART8_IRQn;
300 this->serialPortName = std::string("uart8");
301 break;
302#endif
303 default:
304 LOG_ERR(logger, "USART selected not supported!");
305 D(assert(false && "USART selected not supported!"));
306 }
307}
308
310
312{
313 char c;
314 bool received = false;
315 bool framingError;
316
317#ifndef _ARCH_CORTEXM7_STM32F7
318 // If read data register is empty then read data
319 received = ((usart->SR & USART_SR_RXNE) == 0 ? false : true);
320 // If no error put data in buffer
321 framingError = ((usart->SR & USART_SR_FE) == 0 ? false : true);
322 idle = ((usart->SR & USART_SR_IDLE) == 0 ? false : true);
323 // Always read data, since this clears interrupt flags
324 c = usart->DR;
325#else
326 // If read data register is empty then read data
327 received = ((usart->ISR & USART_ISR_RXNE) == 0 ? false : true);
328 // If no error put data in buffer
329 framingError = ((usart->ISR & USART_ISR_FE) == 0 ? false : true);
330 idle = ((usart->ISR & USART_ISR_IDLE) == 0 ? false : true);
331 // Clears interrupt flags
332 usart->ICR = USART_ICR_IDLECF;
333 // Always read data, since this clears interrupt flags
334 c = usart->RDR;
335#endif
336
337 // If we received some data without framing error but the tryPut failed,
338 // report a FIFO overflow
339 if (framingError || (received && !rxQueue.tryPut(c)))
340 error = true;
341
342 // Wake up thread if communication finished (idle state), buffer reached
343 // half of his capacity or error occurred
344 if (error || idle || (rxQueue.size() >= rxQueue.capacity() / 2))
345 {
346 // Enough data in buffer or idle line, awake thread
347 if (rxWaiter)
348 {
349 rxWaiter->IRQwakeup();
350 if (rxWaiter->IRQgetPriority() >
351 miosix::Thread::IRQgetCurrentThread()->IRQgetPriority())
352 {
353 miosix::Scheduler::IRQfindNextThread();
354 }
355 rxWaiter = nullptr;
356 }
357 }
358}
359
360USART::USART(USARTType* usart, int baudrate, unsigned int queueLen)
361 : USARTInterface(usart, baudrate), rxQueue(queueLen)
362{
363 // Enabling the peripheral on the right APB
365
366 // Setting the baudrate chosen
368
369 // Default settings
370 setStopBits(1);
373 setOversampling(false);
374
375 {
376 miosix::FastInterruptDisableLock dLock;
377
378 // Enable usart, receiver, receiver interrupt and idle interrupt
379 usart->CR1 |= USART_CR1_UE // Enabling the uart peripheral
380 | USART_CR1_RXNEIE // Interrupt on data received
381 | USART_CR1_IDLEIE // interrupt on idle line
382 | USART_CR1_TE // Transmission enabled
383 | USART_CR1_RE; // Reception enabled
384
385 // Sample only one bit
386 usart->CR3 |= USART_CR3_ONEBIT;
387 }
388
389 // Add to the array of usarts so that the interrupts can see it
390 ports[id - 1] = this;
391
392 // Enabling the interrupt for the relative serial port
393 NVIC_SetPriority(irqn, 15);
394 NVIC_EnableIRQ(irqn);
395
396 // Clearing the queue for random data read at the beginning
397 this->clearQueue();
398}
399
401{
402 miosix::FastInterruptDisableLock dLock;
403
404 // Take out the usart object we are going to destruct
405 ports[this->id - 1] = nullptr;
406
407 // Disabling the usart
408 usart->CR1 &= ~(USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
409
410 // Disabling the interrupt of the serial port
411 NVIC_DisableIRQ(irqn);
412}
413
415{
416 miosix::FastInterruptDisableLock dLock;
417 (wordLength == WordLength::BIT8 ? usart->CR1 &= ~USART_CR1_M
418 : usart->CR1 |= USART_CR1_M);
419 this->wordLength = wordLength;
420}
421
423{
424 miosix::FastInterruptDisableLock dLock;
425 (parity == ParityBit::NO_PARITY ? usart->CR1 &= ~USART_CR1_PCE
426 : usart->CR1 |= USART_CR1_PCE);
427 this->parity = parity;
428}
429
430void USART::setStopBits(int stopBits)
431{
432 miosix::FastInterruptDisableLock dLock;
433 this->stopBits = stopBits;
434 usart->CR2 &= ~USART_CR2_STOP;
435 if (stopBits == 2)
436 usart->CR2 |= USART_CR2_STOP_1;
437}
438
439void USART::setOversampling(bool oversampling)
440{
441 miosix::FastInterruptDisableLock dLock;
442 this->over8 = oversampling;
443 (oversampling ? usart->CR1 |= USART_CR1_OVER8
444 : usart->CR1 &= ~USART_CR1_OVER8);
445}
446
447void USART::setBaudrate(int baudrate)
448{
449 /*
450 * Baudrate setting:
451 * fixed point: mantissa first 12 bits, other 4 bits are the fixed point
452 * value of the fraction.
453 * - if over8==0 => DIV_Fraction[3:0] (all fraction used)
454 * USART_DIV = f/(16*baud)
455 * - if over8==1 => 0+DIV_Fraction[2:0] (first bit of fraction is 0)
456 * USART_DIV = f/(8*baud)
457 */
458 miosix::InterruptDisableLock dLock;
459
460 // USART1 and USART6 are always connected to the APB2, while all the others
461 // UART/USART peripherals are always connected to APB1
463 (id == 1 || id == 6 ? ClockUtils::APB::APB2 // High speed APB2
464 : ClockUtils::APB::APB1)); // Low speed APB1
465
466 // <<4 in order to shift to left of 4 positions, to create a fixed point
467 // number of 4 decimal digits /8 == >>3 in order to divide per 8 (from the
468 // formula in the datasheet). So it should be << 1 but, in order to make the
469 // approximation later, we shift it of 2 positions
470 uint32_t brr = ((f << 2) / (((int)baudrate * (over8 ? 1 : 2))));
471
472 // rounding to the nearest
473 usart->BRR = (brr / 2) + (brr & 1);
474
475 this->baudrate = baudrate;
476}
477
478bool USART::readImpl(void* buffer, size_t nBytes, size_t& nBytesRead,
479 const bool blocking, std::chrono::nanoseconds timeout)
480{
481 miosix::Lock<miosix::FastMutex> l(rxMutex);
482
483 char* buf = reinterpret_cast<char*>(buffer);
484 size_t result = 0;
485 error = false;
486 // Whether we timed out while waiting
487 bool timedOut = false;
488
489 miosix::FastInterruptDisableLock dLock;
490 for (;;)
491 {
492 // Try to get all the data possible from the queue
493 for (; result < nBytes; result++)
494 {
495 if (!rxQueue.tryGet(buf[result]))
496 break;
497
498 // This is here just not to keep IRQ disabled for the whole loop
499 miosix::FastInterruptEnableLock eLock(dLock);
500 }
501
502 // If blocking, we are waiting for at least one byte of data before
503 // returning. If not blocking, in the case the bus is idle we return
504 // anyway
505 if ((result == nBytes) || (idle && (!blocking || (result > 0))) ||
506 timedOut)
507 {
508 break;
509 }
510
511 // Wait for data in the queue
512 do
513 {
514 rxWaiter = miosix::Thread::IRQgetCurrentThread();
515
516 if (timeout == std::chrono::nanoseconds::zero())
517 {
518 miosix::Thread::IRQenableIrqAndWait(dLock);
519 }
520 else
521 {
522 int64_t wakeup =
523 std::add_sat(miosix::IRQgetTime(), timeout.count());
524 auto waitResult =
525 miosix::Thread::IRQenableIrqAndTimedWait(dLock, wakeup);
526
527 if (waitResult == miosix::TimedWaitResult::Timeout)
528 {
529 // De-register from wakeup by the IRQ
530 rxWaiter = nullptr;
531 // Make the outer for-loop quit after reading data from the
532 // rxQueue, or we'd end up waiting again
533 timedOut = true;
534 }
535 }
536 } while (rxWaiter);
537 }
538 nBytesRead = result;
539
540 return (result > 0) && !timedOut;
541}
542
543void USART::write(const void* buffer, size_t nBytes)
544{
545 miosix::Lock<miosix::FastMutex> l(txMutex);
546
547 // TODO: Use the send complete interrupt in order not to have a busy while
548 // loop waiting
549 const char* buf = reinterpret_cast<const char*>(buffer);
550 size_t i;
551 for (i = 0; i < nBytes; i++)
552 {
553#ifndef _ARCH_CORTEXM7_STM32F7
554 while ((usart->SR & USART_SR_TXE) == 0)
555 ;
556 usart->DR = *buf++;
557#else
558 while ((usart->ISR & USART_ISR_TXE) == 0)
559 ;
560 usart->TDR = *buf++;
561#endif
562 }
563}
564
565void USART::writeString(const char* buffer)
566{
567 int i = 0;
568 miosix::Lock<miosix::FastMutex> l(txMutex);
569
570 // Send everything, also the ending '\0' character
571#ifndef _ARCH_CORTEXM7_STM32F7
572 usart->DR = *buffer;
573#else
574 usart->TDR = *buffer;
575#endif
576
577 i++;
578
579 while (*buffer != '\0')
580 {
581 buffer++;
582
583#ifndef _ARCH_CORTEXM7_STM32F7
584 while (!(usart->SR & USART_SR_TXE))
585 ;
586 usart->DR = *buffer;
587#else
588 while ((usart->ISR & USART_ISR_TXE) == 0)
589 ;
590 usart->TDR = *buffer;
591#endif
592
593 i++;
594 };
595}
596
597bool USART::writeFile(const std::string& fileName)
598{
599 std::ifstream file(fileName, std::ifstream::binary);
600 std::vector<uint8_t> buffer(1024, 0); // 1024 Bytes buffer.
601
602 if (!file.is_open())
603 {
604 LOG_ERR(logger, "Failed to open file {}", fileName);
605 return false;
606 }
607
608 while (file.read(reinterpret_cast<char*>(buffer.data()), buffer.size()))
609 {
610 std::streamsize s = file.gcount();
611 if (s > 0)
612 write(buffer.data(), static_cast<size_t>(s));
613 }
614 return true;
615}
616
618{
619 char buf[INTERNAL_QUEUE_LENGTH];
620 rxQueue.reset();
621 while (read(buf, INTERNAL_QUEUE_LENGTH))
622 ;
623 rxQueue.reset();
624}
625
627 : USARTInterface(usart, baudrate)
628{
629 if (this->id < 1 || this->id > 4)
630 {
631 LOG_ERR(logger, "USART selected not supported for STM32SerialWrapper!");
632 D(assert(false &&
633 "USART selected not supported for STM32SerialWrapper!"));
634 }
635
636 // Creates and adds the serial port to the devices
637 this->serial = new miosix::STM32Serial(id, baudrate);
638
639 if (!serialCommSetup())
640 {
642 "[STM32SerialWrapper] can't initialize serial communication!");
643 D(assert(false &&
644 "[STM32SerialWrapper] Error : can't initialize serial "
645 "communication!\n"));
646 }
647}
648
650 miosix::GpioPin tx, miosix::GpioPin rx)
651 : USARTInterface(usart, baudrate)
652{
653 if (this->id < 1 || this->id > 4)
654 {
655 LOG_ERR(logger, "USART selected not supported for STM32SerialWrapper!");
656 D(assert(false &&
657 "USART selected not supported for STM32SerialWrapper!"));
658 }
659
660 // Creates and adds the serial port to the devices
661 this->serial = new miosix::STM32Serial(id, baudrate, tx, rx);
662
663 if (!serialCommSetup())
664 {
666 "[STM32SerialWrapper] can't initialize serial communication!");
667 D(assert(false &&
668 "[STM32SerialWrapper] Error : can't initialize serial "
669 "communication!\n"));
670 }
671}
672
674{
675 miosix::intrusive_ref_ptr<miosix::DevFs> devFs =
676 miosix::FilesystemManager::instance().getDevFs();
677 close(fd);
678 devFs->remove(serialPortName.c_str());
679}
680
681bool STM32SerialWrapper::serialCommSetup()
682{
683 // Adds a device to the file system
684 if (!miosix::FilesystemManager::instance().getDevFs()->addDevice(
685 serialPortName.c_str(),
686 miosix::intrusive_ref_ptr<miosix::Device>(serial)))
687 {
688 return false;
689 }
690
691 // Path string "/dev/<name_of_port>" for the port we want to open
692 std::string serialPortPath = "/dev/" + serialPortName;
693
694 // Open serial port
695 fd = open(serialPortPath.c_str(), O_RDWR);
696
697 if (fd <= -1)
698 {
699 TRACE("Cannot open %s\n", serialPortPath.c_str());
700 return false;
701 }
702
703 return true;
704}
705
706bool STM32SerialWrapper::readImpl(void* buffer, size_t nBytes,
707 size_t& nBytesRead, const bool blocking,
708 std::chrono::nanoseconds timeout)
709{
710 // non-blocking read not supported in STM32SerialWrapper
711 if (!blocking)
712 {
714 "STM32SerialWrapper::read doesn't support non-blocking read");
715 D(assert(false &&
716 "STM32SerialWrapper::read doesn't support non-blocking read"));
717 }
718
719 // Timeout is not supported on STM32SerialWrapper, timeout is always set to
720 // std::chrono::nanoseconds::zero() unless specified by the user
721 if (timeout != std::chrono::nanoseconds::zero())
722 {
724 "STM32SerialWrapper::read doesn't support timeout on read");
725 D(assert(false &&
726 "STM32SerialWrapper::read doesn't support timeout on read"));
727 }
728
729 size_t n = ::read(fd, buffer, nBytes);
730 nBytesRead = n;
731
732 return (n > 0);
733}
734
735void STM32SerialWrapper::write(const void* buffer, size_t nBytes)
736{
737 ::write(fd, buffer, nBytes);
738}
739
740void STM32SerialWrapper::writeString(const char* buffer)
741{
742 // strlen + 1 in order to send the '/0' terminated string
743 ::write(fd, buffer, strlen(buffer) + 1);
744}
745
746} // namespace Boardcore
void __attribute__((naked)) CAN1_RX0_IRQHandler()
#define TRACE(...)
Definition Debug.h:58
#define D(x)
Definition Debug.h:57
#define LOG_ERR(logger,...)
Boardcore::USART * ports[N_USART_PORTS]
< Pointer to serial port classes to let interrupts access the classes
Definition USART.cpp:39
USART_TypeDef USARTType
Definition USART.h:36
void write(const void *buf, size_t nBytes)
Blocking write operation.
Definition USART.cpp:735
STM32SerialWrapper(USARTType *usart, int baudrate)
Initializes the serialPortName and initializes the default pins, which are:
Definition USART.cpp:626
~STM32SerialWrapper()
Removes the device from the list of the devices and closes the file of the device.
Definition USART.cpp:673
void writeString(const char *buffer)
Write a string to the serial, comprising the '\0' character.
Definition USART.cpp:740
Driver for STM32F4 low level USART/UART peripheral.
Definition USART.h:172
void setParity(ParityBit pb)
Set the presence of the parity in the data sent.
Definition USART.cpp:422
void IRQhandleInterrupt()
Interrupt handler that deals with receive and idle interrupts.
Definition USART.cpp:311
void setWordLength(WordLength wl)
Set the length of the word to 8 or to 9.
Definition USART.cpp:414
void writeString(const char *buffer)
Write a string to the serial, comprising the '\0' character.
Definition USART.cpp:565
void clearQueue()
Clears the rxQueue.
Definition USART.cpp:617
bool read(void *buffer, size_t nBytes)
Non-blocking read operation to read nBytes or till the data transfer is complete.
Definition USART.h:236
bool writeFile(const std::string &fileName)
Given a filename, uses the USART interface to stream the file in 1KB chunks.
Definition USART.cpp:597
USART(USARTType *usart, int baudrate, unsigned int queueLen=INTERNAL_QUEUE_LENGTH)
Automatically enables the peripheral and timer peripheral clock.
Definition USART.cpp:360
void write(const void *buf, size_t nBytes)
Blocking write operation.
Definition USART.cpp:543
void setStopBits(int stopBits)
Set the number of stop bits.
Definition USART.cpp:430
void setOversampling(bool oversampling)
Sets the Over8 bit.
Definition USART.cpp:439
void setBaudrate(int baudrate)
Set the baudrate in the BRR register.
Definition USART.cpp:447
~USART() override
Disables the flags for the generation of the interrupts, the IRQ from the NVIC, the peripheral and re...
Definition USART.cpp:400
Abstract class that implements the interface for the USART/UART serial communication.
Definition USART.h:70
USARTInterface(USARTType *usart, int baudrate)
Constructor of the USART in order to assign usart and baudrate.
Definition USART.cpp:241
IRQn_Type irqn
IRQ number.
Definition USART.h:155
virtual ~USARTInterface()=0
Definition USART.cpp:309
std::string serialPortName
Definition USART.h:156
bool enablePeripheralClock(void *peripheral)
Enables a peripheral clock source from the APB1 and APB2 peripheral buses.
Definition ClockUtils.h:155
uint32_t getAPBPeripheralsClock(APB bus)
Computes the output clock frequency for peripherals on the given APB.
Definition ClockUtils.h:73
Driver for the VN100S IMU.