mirror of
https://github.com/QuantumLeaps/qpcpp.git
synced 2025-01-28 06:02:56 +08:00
7.0.2
improved Zephyr port and examples
This commit is contained in:
parent
f16303644b
commit
4cdb28e10d
@ -7,7 +7,7 @@
|
||||
#---------------------------------------------------------------------------
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = "QP/C++"
|
||||
PROJECT_NUMBER = 7.0.1
|
||||
PROJECT_NUMBER = 7.0.2
|
||||
PROJECT_BRIEF = "Real-Time Embedded Framework"
|
||||
PROJECT_LOGO = ../../ql-doxygen/images/logo_ql.png
|
||||
OUTPUT_DIRECTORY =
|
||||
|
BIN
doxygen/images/qp-zephyr.jpg
Normal file
BIN
doxygen/images/qp-zephyr.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
@ -1,8 +1,12 @@
|
||||
# QPCPP Zephyr application
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
|
||||
|
||||
CONFIG_QPCPP=y
|
||||
CONFIG_CPLUSPLUS=y
|
||||
CONFIG_ASSERT=y
|
||||
CONFIG_ASSERT_LEVEL=2
|
||||
CONFIG_NUM_COOP_PRIORITIES=29
|
||||
CONFIG_NUM_PREEMPT_PRIORITIES=40
|
||||
CONFIG_GPIO=y
|
||||
CONFIG_REBOOT=y
|
||||
|
||||
# for QSPY...
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_UART_INTERRUPT_DRIVEN=y
|
||||
|
@ -22,8 +22,8 @@
|
||||
// <www.state-machine.com>
|
||||
// <info@state-machine.com>
|
||||
//============================================================================
|
||||
//! @date Last updated on: 2022-08-05
|
||||
//! @version Last updated for: @ref qpcpp_7_0_1
|
||||
//! @date Last updated on: 2022-08-12
|
||||
//! @version Last updated for: @ref qpcpp_7_0_2
|
||||
//!
|
||||
//! @file
|
||||
//! @brief DPP example (BSP)
|
||||
@ -68,7 +68,7 @@ static struct k_timer QF_tick_timer;
|
||||
PHILO_STAT = QP::QS_USER,
|
||||
PAUSED_STAT,
|
||||
COMMAND_STAT,
|
||||
CONTEXT_SW
|
||||
TEST_MSG
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -103,22 +103,20 @@ void BSP::init(void) {
|
||||
|
||||
// object dictionaries...
|
||||
QS_OBJ_DICTIONARY(AO_Table);
|
||||
QS_OBJ_DICTIONARY(AO_Philo[0]);
|
||||
QS_OBJ_DICTIONARY(AO_Philo[1]);
|
||||
QS_OBJ_DICTIONARY(AO_Philo[2]);
|
||||
QS_OBJ_DICTIONARY(AO_Philo[3]);
|
||||
QS_OBJ_DICTIONARY(AO_Philo[4]);
|
||||
QS_OBJ_DICTIONARY(&timerID);
|
||||
for (int n = 0; n < N_PHILO; ++n) {
|
||||
QS_OBJ_ARR_DICTIONARY(AO_Philo[n], n);
|
||||
}
|
||||
|
||||
QS_USR_DICTIONARY(PHILO_STAT);
|
||||
QS_USR_DICTIONARY(PAUSED_STAT);
|
||||
QS_USR_DICTIONARY(COMMAND_STAT);
|
||||
QS_USR_DICTIONARY(CONTEXT_SW);
|
||||
QS_USR_DICTIONARY(TEST_MSG);
|
||||
|
||||
// setup the QS filters...
|
||||
QS_GLB_FILTER(QP::QS_SM_RECORDS); // state machine records
|
||||
QS_GLB_FILTER(QP::QS_AO_RECORDS); // active object records
|
||||
QS_GLB_FILTER(QP::QS_UA_RECORDS); // all user records
|
||||
QS_GLB_FILTER(TEST_MSG);
|
||||
}
|
||||
//............................................................................
|
||||
void BSP::ledOn(void) {
|
||||
@ -136,6 +134,7 @@ void BSP::displayPhilStat(uint8_t n, char const *stat) {
|
||||
else {
|
||||
ledOff();
|
||||
}
|
||||
Q_PRINTK("Philo[%d]->%s\n", n, stat);
|
||||
|
||||
QS_BEGIN_ID(PHILO_STAT, AO_Philo[n]->m_prio) // app-specific record begin
|
||||
QS_U8(1, n); // Philosopher number
|
||||
@ -144,7 +143,7 @@ void BSP::displayPhilStat(uint8_t n, char const *stat) {
|
||||
}
|
||||
//............................................................................
|
||||
void BSP::displayPaused(uint8_t paused) {
|
||||
static_cast<void>(paused); // unused parameter
|
||||
Q_UNUSED_PAR(paused);
|
||||
}
|
||||
//............................................................................
|
||||
uint32_t BSP::random(void) { // a very cheap pseudo-random-number generator
|
||||
@ -167,7 +166,7 @@ void BSP::randomSeed(uint32_t seed) {
|
||||
|
||||
//............................................................................
|
||||
void BSP::terminate(int16_t result) {
|
||||
(void)result;
|
||||
Q_UNUSED_PAR(result);
|
||||
}
|
||||
|
||||
} // namespace DPP
|
||||
@ -179,9 +178,11 @@ namespace QP {
|
||||
// QF callbacks ==============================================================
|
||||
void QF::onStartup(void) {
|
||||
k_timer_start(&QF_tick_timer, K_MSEC(1), K_MSEC(1));
|
||||
Q_PRINTK("QF::onStartup()");
|
||||
}
|
||||
//............................................................................
|
||||
void QF::onCleanup(void) {
|
||||
Q_PRINTK("QF::onCleanup()");
|
||||
}
|
||||
|
||||
// QS callbacks ==============================================================
|
||||
@ -189,52 +190,53 @@ void QF::onCleanup(void) {
|
||||
|
||||
#include <drivers/uart.h>
|
||||
|
||||
static const struct device *uart_console_dev;
|
||||
// select the Zephyr shell UART
|
||||
// NOTE: you can change this to other UART peripheral if desired
|
||||
static struct device const *uart_dev =
|
||||
DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
|
||||
|
||||
//............................................................................
|
||||
static K_THREAD_STACK_DEFINE(qspy_stack, 1024); // stack storage
|
||||
static k_thread qspy_thread_handler;
|
||||
static void qspy_thread(void *p1, void *p2, void *p3){
|
||||
while (true) {
|
||||
// transmit bytes...
|
||||
std::uint16_t len = 0xFFFFU; // get as many bytes as available
|
||||
void QS::doOutput(void) {
|
||||
std::uint16_t len = 0xFFFFU; // big number to get all available bytes
|
||||
|
||||
QS_CRIT_STAT_
|
||||
QS_CRIT_E_();
|
||||
std::uint8_t const *buf = QS::getBlock(&len);
|
||||
QS_CRIT_X_();
|
||||
for (; len != 0U; --len, ++buf) {
|
||||
uart_poll_out(uart_console_dev, *buf);
|
||||
}
|
||||
|
||||
// receive bytes...
|
||||
std::uint8_t b;
|
||||
while (uart_poll_in(uart_console_dev, &b) == 0) {
|
||||
QS::rxPut(b);
|
||||
}
|
||||
QS::rxParse();
|
||||
// transmit the bytes via the UART...
|
||||
for (; len != 0U; --len, ++buf) {
|
||||
uart_poll_out(uart_dev, *buf);
|
||||
}
|
||||
}
|
||||
//............................................................................
|
||||
static void uart_cb(const struct device *dev, void *user_data) {
|
||||
if (!uart_irq_update(uart_dev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (uart_irq_rx_ready(uart_dev)) {
|
||||
std::uint8_t buf[32];
|
||||
int n = uart_fifo_read(uart_dev, buf, sizeof(buf));
|
||||
for (std::uint8_t const *p = buf; n > 0; --n, ++p) {
|
||||
QS::rxPut(*p);
|
||||
}
|
||||
}
|
||||
}
|
||||
//............................................................................
|
||||
bool QS::onStartup(void const *arg) {
|
||||
static uint8_t qsTxBuf[2*1024]; // buffer for QS transmit channel
|
||||
static uint8_t qsRxBuf[256]; // buffer for QS receive channel
|
||||
|
||||
Q_REQUIRE(uart_dev != NULL);
|
||||
|
||||
initBuf (qsTxBuf, sizeof(qsTxBuf));
|
||||
rxInitBuf(qsRxBuf, sizeof(qsRxBuf));
|
||||
uart_console_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
|
||||
Q_ASSERT(uart_console_dev != NULL);
|
||||
k_thread_create(&qspy_thread_handler,
|
||||
qspy_stack,
|
||||
K_THREAD_STACK_SIZEOF(qspy_stack),
|
||||
&qspy_thread,
|
||||
nullptr, // p1
|
||||
nullptr, // p2
|
||||
nullptr, // p3
|
||||
QF_MAX_ACTIVE, // lowest priority
|
||||
K_ESSENTIAL, // thread options
|
||||
K_NO_WAIT); // start immediately
|
||||
//TODO assert if could not create thread
|
||||
|
||||
// configure interrupt and callback to receive data
|
||||
uart_irq_callback_user_data_set(uart_dev, &uart_cb, nullptr);
|
||||
uart_irq_rx_enable(uart_dev);
|
||||
|
||||
return true; // return success
|
||||
}
|
||||
//............................................................................
|
||||
@ -242,19 +244,17 @@ void QS::onCleanup(void) {
|
||||
}
|
||||
//............................................................................
|
||||
QSTimeCtr QS::onGetTime(void) { // NOTE: invoked with interrupts DISABLED
|
||||
return k_uptime_get_32();
|
||||
return k_cycle_get_32();
|
||||
}
|
||||
//............................................................................
|
||||
void QS::onFlush(void) {
|
||||
uint16_t len = 0xFFFFU; // big number to get as many bytes as available
|
||||
uint8_t const *buf = QS::getBlock(&len); // get continguous block of data
|
||||
while (buf != nullptr) { // data available?
|
||||
for(auto i = 0;i!=len;i++)
|
||||
{
|
||||
uart_poll_out(uart_console_dev,buf[i]);
|
||||
std::uint16_t len = 0xFFFFU; // to get as many bytes as available
|
||||
std::uint8_t const *buf;
|
||||
while ((buf = QS::getBlock(&len)) != nullptr) { // QS-TX data available?
|
||||
for (; len != 0U; --len, ++buf) {
|
||||
uart_poll_out(uart_dev, *buf);
|
||||
}
|
||||
len = 0xFFFFU; // big number to get as many bytes as available
|
||||
buf = QS::getBlock(&len); // try to get more data
|
||||
len = 0xFFFFU; // to get as many bytes as available
|
||||
}
|
||||
}
|
||||
//............................................................................
|
||||
@ -268,10 +268,10 @@ extern "C" void assert_failed(char const *module, int loc);
|
||||
void QS::onCommand(uint8_t cmdId, uint32_t param1,
|
||||
uint32_t param2, uint32_t param3)
|
||||
{
|
||||
(void)cmdId;
|
||||
(void)param1;
|
||||
(void)param2;
|
||||
(void)param3;
|
||||
Q_UNUSED_PAR(cmdId);
|
||||
Q_UNUSED_PAR(param1);
|
||||
Q_UNUSED_PAR(param2);
|
||||
Q_UNUSED_PAR(param3);
|
||||
}
|
||||
|
||||
#endif // Q_SPY
|
||||
@ -286,8 +286,8 @@ Q_NORETURN Q_onAssert(char const * const module, int_t const loc) {
|
||||
//
|
||||
// NOTE: add here your application-specific error handling
|
||||
//
|
||||
printk("\nASSERTION in %s:%d\n", module, loc);
|
||||
QS_ASSERTION(module, loc, static_cast<uint32_t>(10000U));
|
||||
Q_PRINTK("\nASSERTION in %s:%d\n", module, loc);
|
||||
|
||||
#ifndef NDEBUG
|
||||
k_panic(); // debug build: halt the system for error search...
|
||||
|
@ -32,7 +32,8 @@ zephyr_library_sources(
|
||||
${QPCPP_DIR}/zephyr/qf_port.cpp
|
||||
)
|
||||
|
||||
if(CONFIG_QSPY)
|
||||
# QSPY option...
|
||||
if(QSPY)
|
||||
|
||||
target_compile_definitions(app PUBLIC Q_SPY)
|
||||
zephyr_library_compile_definitions(Q_SPY)
|
||||
@ -46,6 +47,6 @@ zephyr_library_sources(
|
||||
${QPCPP_DIR}/include/qstamp.cpp
|
||||
)
|
||||
|
||||
endif() # CONFIG_QSPY
|
||||
endif() # QSPY
|
||||
|
||||
endif() # CONFIG_QPCPP
|
||||
|
@ -9,12 +9,6 @@ menuconfig QPCPP
|
||||
|
||||
if QPCPP
|
||||
|
||||
config QSPY
|
||||
bool "QSPY Tracing"
|
||||
default n
|
||||
help
|
||||
Enables the QSPY Tracing for QP
|
||||
|
||||
module = QPCPP
|
||||
module-str = QPCPP
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
# qpcpp Zephyr Module
|
||||
[![QP Zephyr Module](../doxygen/images/qp-zephyr.jpg)](https://www.state-machine.com/qpcpp/zephyr.html)
|
||||
|
||||
# About the QPCPP Zephyr Module
|
||||
This directory defines the
|
||||
[QP/C++ Real-Time Embedded Framework](https://github.com/QuantumLeaps/qpcpp)
|
||||
as a [Zephyr module](https://docs.zephyrproject.org/latest/develop/modules.html).
|
||||
@ -7,22 +9,32 @@ as a [Zephyr module](https://docs.zephyrproject.org/latest/develop/modules.html)
|
||||
Example of use is provided in the related repository
|
||||
[qpcpp-zephyr-app](https://github.com/QuantumLeaps/qpcpp-zephyr-app)
|
||||
|
||||
To create your own QP-Zephyr project, you can clone that repository
|
||||
and customize it to your needs:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/QuantumLeaps/qpcpp-zephyr-app <my-project> --recurse-submodules --depth 1
|
||||
```
|
||||
where `<my-project>` is the name of your project.
|
||||
|
||||
|
||||
## Configuring the QPCPP Zephyr Module
|
||||
The `Kconfig` file provides configuration `CONFIG_QPCPP` to activate
|
||||
the QPCPP module in Zephyr. To do so, you need to add the following
|
||||
line to your `prj.conf`:
|
||||
The `Kconfig` file provides configuration `CONFIG_QPCPP` to activate the QPCPP module
|
||||
in Zephyr. To do so, you need to add the following line to your `prj.conf`:
|
||||
|
||||
```ini
|
||||
CONFIG_QPCPP=y
|
||||
```
|
||||
|
||||
## Configuring the QSPY Software Tracing
|
||||
If you wish to enable
|
||||
[QSPY Software Tracing](https://www.state-machine.com/qtools/qpspy.html),
|
||||
`Kconfig` file provides configuration `CONFIG_QSPY`, which you can
|
||||
use in your `prj.conf`:
|
||||
## Option for Activating QSPY Software Tracing
|
||||
The QP/C++ Zephyr Module supports the
|
||||
[QSPY Software Tracing](https://www.state-machine.com/qtools/qpspy.html)
|
||||
option and will add the appropriate macros and files to build the "QSPY"
|
||||
configuration.
|
||||
|
||||
```ini
|
||||
CONFIG_QSPY=y
|
||||
If you wish to enable "QSPY" you can provide the option "QSPY"
|
||||
in the command-line for the build. For example:
|
||||
|
||||
```bash
|
||||
west build -b nucleo_h743zi -- -DQSPY=ON
|
||||
```
|
||||
|
@ -22,8 +22,8 @@
|
||||
// <www.state-machine.com>
|
||||
// <info@state-machine.com>
|
||||
//============================================================================
|
||||
//! @date Last updated on: 2022-08-06
|
||||
//! @version Last updated for: @ref qpcpp_7_0_1
|
||||
//! @date Last updated on: 2022-08-12
|
||||
//! @version Last updated for: @ref qpcpp_7_0_2
|
||||
//!
|
||||
//! @file
|
||||
//! @brief QF/C++ port to Zephyr RTOS kernel, all supported compilers
|
||||
@ -67,7 +67,18 @@ void QF::init(void) {
|
||||
//............................................................................
|
||||
int_t QF::run(void) {
|
||||
onStartup();
|
||||
#ifdef Q_SPY
|
||||
// lower the priority of the main thread to the level of idle
|
||||
k_thread_priority_set(k_current_get(),
|
||||
CONFIG_NUM_PREEMPT_PRIORITIES - 1);
|
||||
// perform QS work
|
||||
while (true) {
|
||||
QS::rxParse(); // parse any QS-RX bytes
|
||||
QS::doOutput(); // perform the QS-TX output
|
||||
}
|
||||
#else
|
||||
return 0; // return from the main Zephyr thread
|
||||
#endif
|
||||
}
|
||||
//............................................................................
|
||||
void QF::stop(void) {
|
||||
|
@ -22,8 +22,8 @@
|
||||
// <www.state-machine.com>
|
||||
// <info@state-machine.com>
|
||||
//============================================================================
|
||||
//! @date Last updated on: 2022-08-06
|
||||
//! @version Last updated for: @ref qpcpp_7_0_1
|
||||
//! @date Last updated on: 2022-08-12
|
||||
//! @version Last updated for: @ref qpcpp_7_0_2
|
||||
//!
|
||||
//! @file
|
||||
//! @brief QF/C++ port to Zephyr RTOS kernel, all supported compilers
|
||||
@ -61,6 +61,14 @@ extern struct k_spinlock spinlock;
|
||||
} // namespace QF
|
||||
} // namespace QP
|
||||
|
||||
// Q_PRINTK() macro to avoid conflicts with Zephyr's printk()
|
||||
// when Q_SPY configuation is used
|
||||
#ifndef Q_SPY
|
||||
#define Q_PRINTK(fmt_, ...) printk(fmt_, ##__VA_ARGS__)
|
||||
#else
|
||||
#define Q_PRINTK(dummy, ...) (static_cast<void>(0))
|
||||
#endif
|
||||
|
||||
//============================================================================
|
||||
// interface used only inside QF, but not in applications
|
||||
//
|
||||
|
Loading…
x
Reference in New Issue
Block a user