You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
8.6 KiB
C++
238 lines
8.6 KiB
C++
|
8 months ago
|
//============================================================================
|
||
|
|
// Product: QUTEST port for STM32 NUCLEO-C031C6 board
|
||
|
|
//
|
||
|
|
// Q u a n t u m L e a P s
|
||
|
|
// ------------------------
|
||
|
|
// Modern Embedded Software
|
||
|
|
//
|
||
|
|
// Copyright (C) 2005 Quantum Leaps, LLC. <state-machine.com>
|
||
|
|
//
|
||
|
|
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
|
||
|
|
//
|
||
|
|
// This software is dual-licensed under the terms of the open source GNU
|
||
|
|
// General Public License version 3 (or any later version), or alternatively,
|
||
|
|
// under the terms of one of the closed source Quantum Leaps commercial
|
||
|
|
// licenses.
|
||
|
|
//
|
||
|
|
// The terms of the open source GNU General Public License version 3
|
||
|
|
// can be found at: <www.gnu.org/licenses/gpl-3.0>
|
||
|
|
//
|
||
|
|
// The terms of the closed source Quantum Leaps commercial licenses
|
||
|
|
// can be found at: <www.state-machine.com/licensing>
|
||
|
|
//
|
||
|
|
// Redistributions in source code must retain this top-level comment block.
|
||
|
|
// Plagiarizing this software to sidestep the license obligations is illegal.
|
||
|
|
//
|
||
|
|
// Contact information:
|
||
|
|
// <www.state-machine.com>
|
||
|
|
// <info@state-machine.com>
|
||
|
|
//============================================================================
|
||
|
|
#ifndef Q_SPY
|
||
|
|
#error "Q_SPY must be defined to compile qutest_cpp.cpp"
|
||
|
|
#endif // Q_SPY
|
||
|
|
|
||
|
|
#define QP_IMPL // this is QP implementation
|
||
|
|
#include "qp_port.hpp" // QP port
|
||
|
|
#include "qs_port.hpp" // QS port
|
||
|
|
#include "qs_pkg.hpp" // QS package-scope interface
|
||
|
|
#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem
|
||
|
|
|
||
|
|
#include "stm32c0xx.h" // CMSIS-compliant header file for the MCU used
|
||
|
|
// add other drivers if necessary...
|
||
|
|
|
||
|
|
//Q_DEFINE_THIS_MODULE("qutest_port")
|
||
|
|
|
||
|
|
using namespace QP;
|
||
|
|
|
||
|
|
// Local-scope defines -------------------------------------------------------
|
||
|
|
// LED pins available on the board (just one user LED LD4--Green on PA.5)
|
||
|
|
#define LD4_PIN 5U
|
||
|
|
|
||
|
|
// Button pins available on the board (just one user Button B1 on PC.13)
|
||
|
|
#define B1_PIN 13U
|
||
|
|
|
||
|
|
static uint16_t const UARTPrescTable[12] = {
|
||
|
|
1U, 2U, 4U, 6U, 8U, 10U, 12U, 16U, 32U, 64U, 128U, 256U
|
||
|
|
};
|
||
|
|
|
||
|
|
#define UART_DIV_SAMPLING16(__PCLK__, __BAUD__, __CLOCKPRESCALER__) \
|
||
|
|
((((__PCLK__)/UARTPrescTable[(__CLOCKPRESCALER__)]) \
|
||
|
|
+ ((__BAUD__)/2U)) / (__BAUD__))
|
||
|
|
|
||
|
|
#define UART_PRESCALER_DIV1 0U
|
||
|
|
|
||
|
|
// USART2 pins PA.2 and PA.3
|
||
|
|
#define USART2_TX_PIN 2U
|
||
|
|
#define USART2_RX_PIN 3U
|
||
|
|
|
||
|
|
//............................................................................
|
||
|
|
extern "C" {
|
||
|
|
|
||
|
|
// ISR for receiving bytes from the QSPY Back-End
|
||
|
|
// NOTE: This ISR is "QF-unaware" meaning that it does not interact with
|
||
|
|
// the QF/QK and is not disabled. Such ISRs don't need to call QK_ISR_ENTRY/
|
||
|
|
// QK_ISR_EXIT and they cannot post or publish events.
|
||
|
|
void USART2_IRQHandler(void); // prototype
|
||
|
|
void USART2_IRQHandler(void) { // used in QS-RX (kernel UNAWARE interrupt)
|
||
|
|
// is RX register NOT empty?
|
||
|
|
while ((USART2->ISR & (1U << 5U)) != 0U) {
|
||
|
|
std::uint32_t b = USART2->RDR;
|
||
|
|
QP::QS::rxPut(b);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//............................................................................
|
||
|
|
void assert_failed(char const * const module, int_t const id); // prototype
|
||
|
|
void assert_failed(char const * const module, int_t const id) {
|
||
|
|
Q_onError(module, id);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // extern "C"
|
||
|
|
|
||
|
|
//============================================================================
|
||
|
|
// QS callbacks...
|
||
|
|
|
||
|
|
//............................................................................
|
||
|
|
bool QS::onStartup(void const *arg) {
|
||
|
|
Q_UNUSED_PAR(arg);
|
||
|
|
|
||
|
|
static std::uint8_t qsTxBuf[2*1024]; // buffer for QS-TX channel
|
||
|
|
initBuf(qsTxBuf, sizeof(qsTxBuf));
|
||
|
|
|
||
|
|
static std::uint8_t qsRxBuf[256]; // buffer for QS-RX channel
|
||
|
|
rxInitBuf(qsRxBuf, sizeof(qsRxBuf));
|
||
|
|
|
||
|
|
// NOTE: SystemInit() already called from the startup code
|
||
|
|
// but SystemCoreClock needs to be updated
|
||
|
|
SystemCoreClockUpdate();
|
||
|
|
|
||
|
|
// enable GPIOA clock port for the LED LD4
|
||
|
|
RCC->IOPENR |= (1U << 0U);
|
||
|
|
|
||
|
|
// set all used GPIOA pins as push-pull output, no pull-up, pull-down
|
||
|
|
GPIOA->MODER &= ~(3U << 2U*LD4_PIN);
|
||
|
|
GPIOA->MODER |= (1U << 2U*LD4_PIN);
|
||
|
|
GPIOA->OTYPER &= ~(1U << LD4_PIN);
|
||
|
|
GPIOA->OSPEEDR &= ~(3U << 2U*LD4_PIN);
|
||
|
|
GPIOA->OSPEEDR |= (1U << 2U*LD4_PIN);
|
||
|
|
GPIOA->PUPDR &= ~(3U << 2U*LD4_PIN);
|
||
|
|
|
||
|
|
// enable GPIOC clock port for the Button B1
|
||
|
|
RCC->IOPENR |= (1U << 2U);
|
||
|
|
|
||
|
|
// configure Button B1 pin on GPIOC as input, no pull-up, pull-down
|
||
|
|
GPIOC->MODER &= ~(3U << 2U*B1_PIN);
|
||
|
|
GPIOC->PUPDR &= ~(3U << 2U*B1_PIN);
|
||
|
|
|
||
|
|
// enable peripheral clock for USART2
|
||
|
|
RCC->IOPENR |= ( 1U << 0U); // Enable GPIOA clock for USART pins
|
||
|
|
RCC->APBENR1 |= ( 1U << 17U); // Enable USART#2 clock
|
||
|
|
|
||
|
|
// Configure PA to USART2_RX, PA to USART2_TX
|
||
|
|
GPIOA->AFR[0] &= ~((15U << 4U*USART2_RX_PIN) | (15U << 4U*USART2_TX_PIN));
|
||
|
|
GPIOA->AFR[0] |= (( 1U << 4U*USART2_RX_PIN) | ( 1U << 4U*USART2_TX_PIN));
|
||
|
|
GPIOA->MODER &= ~(( 3U << 2U*USART2_RX_PIN) | ( 3U << 2U*USART2_TX_PIN));
|
||
|
|
GPIOA->MODER |= (( 2U << 2U*USART2_RX_PIN) | ( 2U << 2U*USART2_TX_PIN));
|
||
|
|
|
||
|
|
// baud rate
|
||
|
|
USART2->BRR = UART_DIV_SAMPLING16(
|
||
|
|
SystemCoreClock, 115200U, UART_PRESCALER_DIV1);
|
||
|
|
USART2->CR3 = 0x0000U | // no flow control
|
||
|
|
(1U << 12U); // disable overrun detection (OVRDIS)
|
||
|
|
USART2->CR2 = 0x0000U; // 1 stop bit
|
||
|
|
USART2->CR1 = ((1U << 2U) | // enable RX
|
||
|
|
(1U << 3U) | // enable TX
|
||
|
|
(1U << 5U) | // enable RX interrupt
|
||
|
|
(0U << 12U) | // 8 data bits
|
||
|
|
(0U << 28U) | // 8 data bits
|
||
|
|
(1U << 0U)); // enable USART
|
||
|
|
|
||
|
|
// explicitly set NVIC priorities of all Cortex-M interrupts used
|
||
|
|
NVIC_SetPriorityGrouping(0U);
|
||
|
|
NVIC_SetPriority(USART2_IRQn, 0U);
|
||
|
|
|
||
|
|
// enable the UART RX interrupt...
|
||
|
|
NVIC_EnableIRQ(USART2_IRQn); // UART2 interrupt used for QS-RX
|
||
|
|
|
||
|
|
return true; // success
|
||
|
|
}
|
||
|
|
//............................................................................
|
||
|
|
void QS::onCleanup(void) {
|
||
|
|
// wait as long as the UART is busy
|
||
|
|
while ((USART2->ISR & (1U << 7U)) == 0U) {
|
||
|
|
}
|
||
|
|
// delay before returning to allow all produced QS bytes to be received
|
||
|
|
for (std::uint32_t volatile dly_ctr = 10000U; dly_ctr > 0U; --dly_ctr) {
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//............................................................................
|
||
|
|
// NOTE:
|
||
|
|
// No critical section in QS::onFlush() to avoid nesting of critical sections
|
||
|
|
// in case QS_onFlush() is called from Q_onError().
|
||
|
|
void QS::onFlush(void) {
|
||
|
|
for (;;) {
|
||
|
|
std::uint16_t b = getByte();
|
||
|
|
if (b != QS_EOD) {
|
||
|
|
while ((USART2->ISR & (1U << 7U)) == 0U) {
|
||
|
|
}
|
||
|
|
USART2->TDR = static_cast<std::uint8_t>(b); // put into the DR register
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//............................................................................
|
||
|
|
//! callback function to reset the target (to be implemented in the BSP)
|
||
|
|
void QS::onReset(void) {
|
||
|
|
NVIC_SystemReset();
|
||
|
|
}
|
||
|
|
//............................................................................
|
||
|
|
void QS::doOutput(void) {
|
||
|
|
if ((USART2->ISR & (1U << 7U)) != 0U) { // is TXE empty?
|
||
|
|
QF_INT_DISABLE();
|
||
|
|
std::uint16_t b = getByte();
|
||
|
|
QF_INT_ENABLE();
|
||
|
|
|
||
|
|
if (b != QS_EOD) { // not End-Of-Data?
|
||
|
|
USART2->TDR = static_cast<std::uint8_t>(b);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//............................................................................
|
||
|
|
void QS::onTestLoop() {
|
||
|
|
rxPriv_.inTestLoop = true;
|
||
|
|
while (rxPriv_.inTestLoop) {
|
||
|
|
|
||
|
|
// toggle an LED LD2 on and then off (not enough LEDs, see NOTE02)
|
||
|
|
GPIOA->BSRR = (1U << LD4_PIN); // turn LED[n] on
|
||
|
|
GPIOA->BSRR = (1U << (LD4_PIN + 16U)); // turn LED[n] off
|
||
|
|
|
||
|
|
rxParse(); // parse all the received bytes
|
||
|
|
|
||
|
|
if ((USART2->ISR & (1U << 7U)) != 0U) { // is TXE empty?
|
||
|
|
QF_INT_DISABLE();
|
||
|
|
uint16_t b = getByte();
|
||
|
|
QF_INT_ENABLE();
|
||
|
|
|
||
|
|
if (b != QS_EOD) { // not End-Of-Data?
|
||
|
|
USART2->TDR = static_cast<std::uint8_t>(b);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// set inTestLoop to true in case calls to QS_onTestLoop() nest,
|
||
|
|
// which can happen through the calls to QS_TEST_PAUSE().
|
||
|
|
rxPriv_.inTestLoop = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//============================================================================
|
||
|
|
// NOTE0:
|
||
|
|
// ARM Cortex-M0+ does NOT provide "kernel-unaware" interrupts, and
|
||
|
|
// consequently *all* interrupts are "kernel-aware". This means that
|
||
|
|
// the UART interrupt used for QS-RX is frequently DISABLED (e.g., to
|
||
|
|
// perform QS-TX). That can lead to lost some of the received bytes, and
|
||
|
|
// consequently some QUTest tests might be failing.
|
||
|
|
// A fix for that would be to use DMA for handling QS-RX, but this is
|
||
|
|
// currently not implemented.
|
||
|
|
//
|