mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-03-29 00:22:13 +00:00
Broke up Create-a-simple-app into multable wikis
parent
d4ad533c65
commit
951f3e1f06
@ -1,8 +1,10 @@
|
||||
This simple application will explain the basic of widgets and simple navigation.
|
||||
## About
|
||||
|
||||
This wiki will go over how to create a simple application and show you the basics in the Mayhem code base. This is to be considered a "hello world" application. :)
|
||||
|
||||
We are going to create few simple controls and link the app to the main menu. The idea is to get familiar with the way things work.
|
||||
|
||||
# First steps
|
||||
## First steps
|
||||
|
||||
There is a lot of flexibility, but for now we are going to follow patterns already found on the source code. For example, the name of the files and classes will be inspired by existing code.
|
||||
|
||||
@ -98,13 +100,12 @@ set(CPPSRC
|
||||
|
||||
In this moment you should be able to compile and test the app in your device. For your reference here's the link to the [Compile-firmware](https://github.com/eried/portapack-mayhem/wiki/Compile-firmware) wiki. The new app should appear in the menu you picked in `ui_navigation.cpp`.
|
||||
|
||||
# Adding functionality
|
||||
## Adding Widgets and Basic Functionality
|
||||
|
||||
## Widgets
|
||||
Widgets are the elements that compose the UI of your custom app. Widgets are defined inside [`firmware\common\ui_widget.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui_widget.hpp) and widget functions can be found inside [`firmware\common\ui_widget.cpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui_widget.cpp). In order to be able to use them, you must `#include "ui_widget.hpp"` into your app .hpp file.
|
||||
There are different type of widget. here you will find a list of available widget, with their respecting constructor. For all the methods available, you should go and see the [ui_widget.hpp](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui_widget.hpp) file.
|
||||
|
||||
### Attach a generic widget to you application
|
||||
### Attach a Generic Widget to Your Application
|
||||
|
||||
In order to display a widget into your app, you can either use the function `add_child()` or `add_children()`.
|
||||
Both those functions shall be called within the code of your `NewAppGameView(NavigationView &nav){}` constructor. The difference between the two function is simple: the first one allows you to add a single widget, while the second one allows you to add an undefined number of widgets.
|
||||
@ -120,25 +121,9 @@ add_children({
|
||||
});
|
||||
```
|
||||
|
||||
### Available widgets
|
||||
There are several different widgets, and more might be added, so you should always go and check whether new widgets have been added or not. Here you will find a list of most basic widgets.
|
||||
There are several different widgets that we're going to use for our new app. A more complete list can be found on the [Widgets](https://github.com/eried/portapack-mayhem/wiki/Widgets) wiki. More might be added so you should always go and check whether new widgets have been added or not.
|
||||
|
||||
#### Text
|
||||
The text widgets add a simple text area to your app. Here you can find it's declaration and prototype:
|
||||
```
|
||||
Text my_text_widget{
|
||||
Rect parent_rect,
|
||||
std::string text
|
||||
};
|
||||
|
||||
```
|
||||
To be noted that `Rect parent_rect` has it's own definition inside another file, but let's say that you would like to add a text widget with the text "Hello World", positioned at the top left corner (spaced 10 from both top margin and left margin), with width 100 and height 24, you cold do it in this way:
|
||||
```
|
||||
Text hello_world_text_widget(
|
||||
{10, 10, 100, 24}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
|
||||
"Hello world!"
|
||||
);
|
||||
```
|
||||
#### Button
|
||||
|
||||
Buttons allows you to do something when you press them. Here you can find it's declaration and prototype:
|
||||
@ -158,6 +143,7 @@ Button my_button(
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
#### Labels
|
||||
|
||||
Labels are a text element that can be used to describe other widgets. Here you can find it's declaration and prototype:
|
||||
@ -190,6 +176,7 @@ NewAppView::NewAppView(NavigationView &nav) {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### LiveDateTime
|
||||
|
||||
LiveDateTime gives you the dynamic date and time. Here you can find it's declaration and prototype:
|
||||
@ -222,40 +209,7 @@ If you want to enable seconds you'll need use the `set_seconds_enabled(bool new_
|
||||
```
|
||||
my_liveDateTime.set_seconds_enabled(true);
|
||||
```
|
||||
#### BigFrequency
|
||||
|
||||
BigFrequency is used for displaying a radio frequency. Here you can find it's declaration and prototype:
|
||||
```
|
||||
Labels my_bigFrequency_widget{
|
||||
Rect parent_rect,
|
||||
rf::Frequency frequency
|
||||
};
|
||||
```
|
||||
|
||||
For example, let's say you want a label called `my_bigFrequency`. You will need to add this to `apps/ui_newapp.hpp`:
|
||||
```
|
||||
Labels my_bigFrequency(
|
||||
{10, 10, 28*8, 52}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
|
||||
0 // Beginning frequency in hz
|
||||
);
|
||||
```
|
||||
|
||||
In `apps/ui_newapp.cpp` you'll need to add the `my_bigFrequency` pointer to add_child() or add_children():
|
||||
```
|
||||
NewAppView::NewAppView(NavigationView &nav) {
|
||||
|
||||
// Widget pointers
|
||||
add_children({
|
||||
&my_bigFrequency,
|
||||
});
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
To set a frequency you can use the function `set(const rf::Frequency frequency)`:
|
||||
```
|
||||
my_bigFrequency.set(433000000); // 433MHz
|
||||
```
|
||||
|
||||
#### ProgressBar
|
||||
|
||||
@ -295,199 +249,6 @@ To change the value of progress for the progress bar use the `set_value(const ui
|
||||
my_progressBar.set_value(5); // 50% Complete
|
||||
```
|
||||
|
||||
#### Console
|
||||
|
||||
Console can be used as large text field where you can output mutable lines of text. Here you can find it's declaration and prototype:
|
||||
```
|
||||
Console my_console_widget{
|
||||
Rect parent_rect
|
||||
};
|
||||
```
|
||||
|
||||
For example, let's say you want a label called `my_console`. You will need to add this to `apps/ui_newapp.hpp`:
|
||||
```
|
||||
Console my_console {
|
||||
{ 2*8, 10, 208, 200 }, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
|
||||
};
|
||||
```
|
||||
|
||||
In `apps/ui_newapp.cpp` you'll need to add the `my_console` pointer to add_child() or add_children():
|
||||
```
|
||||
NewAppView::NewAppView(NavigationView &nav) {
|
||||
|
||||
// Widget pointers
|
||||
add_children({
|
||||
&my_console,
|
||||
});
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
To write to 'my_console' you'll need to use the `write(std::string message)` function:
|
||||
```
|
||||
my_console.write("Hello World");
|
||||
```
|
||||
|
||||
For automatic new line, you can also use the `writeln(std::string message)` function if you don't want to add a `\n' at the end of every string:
|
||||
```
|
||||
my_console.writeln("Hello World but on a new line!");
|
||||
```
|
||||
|
||||
To enable scrolling you can use the `enable_scrolling(bool enable)` function::
|
||||
```
|
||||
my_console.enable_scrolling(true);
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
#### Checkbox
|
||||
|
||||
Checkboxs are a boolean (True/False) widget that allows you chose between one of two options . Here you can find it's declaration and prototype:
|
||||
```
|
||||
Checkbox my_checkbox_widget{
|
||||
Point parent_pos,
|
||||
size_t length,
|
||||
std::string text,
|
||||
bool small
|
||||
};
|
||||
```
|
||||
|
||||
For example, let's say you want a checkbox called `my_checkbox`. You will need to add this to `apps/ui_newapp.hpp`:
|
||||
```
|
||||
Checkbox my_checkbox(
|
||||
{10, 20}, // Coordinates are: int:x (px), int:y (px)
|
||||
4, // Length
|
||||
"my_checkbox_text", // Title
|
||||
false // Checkbox Size: true == small(16X16px), false == regular(24X24px)
|
||||
);
|
||||
```
|
||||
|
||||
In `apps/ui_newapp.cpp` you'll need to add the `my_checkbox` pointer to add_child() or add_children():
|
||||
```
|
||||
NewAppView::NewAppView(NavigationView &nav) {
|
||||
|
||||
// Widget pointers
|
||||
add_children({
|
||||
&my_checkbox,
|
||||
});
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Functions within `apps/ui_newapp.cpp` are able to lookup the value of `my_checkbox` with Checkbox's `value()` function:
|
||||
```
|
||||
if(my_checkbox.value()) {
|
||||
do_a(); // If checkbox is selected (green check mark)
|
||||
} else {
|
||||
do_b(); // If checkbox is NOT selected (red X)
|
||||
}
|
||||
```
|
||||
|
||||
You can also set the value for `my_checkbox` with the `set_value(const bool value)` function:
|
||||
```
|
||||
my_checkbox.set_value(true); // Checkbox selected (green check mark)
|
||||
my_checkbox.set_value(false); // Checkbox deselected (red X)
|
||||
```
|
||||
|
||||
If you want your checkbox to automatically perform an action when toggled you can add this [Lambda](https://www.geeksforgeeks.org/lambda-expression-in-c/) to `apps/ui_newapp.cpp`:
|
||||
```
|
||||
NewAppView::NewAppView(NavigationView &nav) {
|
||||
|
||||
// Add widget pointers
|
||||
add_children({
|
||||
&my_checkbox,
|
||||
});
|
||||
|
||||
// When checkbox is toggled do...
|
||||
my_checkbox.on_select = [this](Checkbox&, bool v) {
|
||||
if(v){
|
||||
do_a(); // If checkbox is selected (green check mark)
|
||||
} else {
|
||||
do_b(); // If checkbox is NOT selected (red X)
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### Image
|
||||
|
||||
Images can be displayed within your app. Here you can find it's declaration and prototype:
|
||||
```
|
||||
Image my_Image_widget{
|
||||
Rect parent_rect,
|
||||
Bitmap* bitmap,
|
||||
Color foreground,
|
||||
Color background
|
||||
};
|
||||
```
|
||||
|
||||
Images need to be a Bitmap object before they can be displayed. Bellow is an example of the code needed to create a Bitmap from [`firmware/application/bitmap.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/bitmap.hpp).
|
||||
```
|
||||
static constexpr uint8_t bitmap_stripes_data[] = {
|
||||
0xFF, 0x03, 0xC0,
|
||||
0xFF, 0x01, 0xE0,
|
||||
0xFF, 0x00, 0xF0,
|
||||
0x7F, 0x00, 0xF8,
|
||||
0x3F, 0x00, 0xFC,
|
||||
0x1F, 0x00, 0xFE,
|
||||
0x0F, 0x00, 0xFF,
|
||||
0x07, 0x80, 0xFF,
|
||||
};
|
||||
static constexpr Bitmap bitmap_stripes {
|
||||
{ 24, 8 }, bitmap_stripes_data
|
||||
};
|
||||
```
|
||||
|
||||
With the Bitmap object created we can now define the image `my_image`. You will need to add this to `apps/ui_newapp.hpp`:
|
||||
```
|
||||
Image my_image(
|
||||
{10, 10, 24, 8}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
|
||||
&Bitmap, // Pointer to your bitmap
|
||||
Color::white(), // Color Foreground
|
||||
Color::black() // Color Background
|
||||
);
|
||||
```
|
||||
**Note:** Colors are defined in [`firmware/common/ui.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui.hpp)
|
||||
|
||||
|
||||
In `apps/ui_newapp.cpp` you'll need to add the `my_image` pointer to add_child() or add_children():
|
||||
```
|
||||
NewAppView::NewAppView(NavigationView &nav) {
|
||||
|
||||
// Widget pointers
|
||||
add_children({
|
||||
&my_image,
|
||||
});
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### OptionsField
|
||||
|
||||
OptionsField is a widget that allows you to create a field, in which you can change its value with the wheel on your portapack. Here you can find it's declaration and prototype:
|
||||
```
|
||||
OptionsField my_OptionsField_widget{
|
||||
Point parent_pos,
|
||||
int length,
|
||||
options_t options
|
||||
};
|
||||
```
|
||||
parent_pos is an array of two integer which tells where the top left corner of the widget should be positioned. The length is an integer which tells how many options you have into your options parameter. The options_t field is an array of options in which your portapack can choose to display.
|
||||
For example, let's say you want an optionsFild called `my_optionsField`, with 3 options, positioned at 10 from top and 10 from left:
|
||||
```
|
||||
OptionsField my_optionsField{
|
||||
{10,10}, // Coordinates are: int:x (px), int:y (px)
|
||||
7, // Char length for option title
|
||||
{
|
||||
{"option1",0},
|
||||
{"option2",1}, // Options {"KEY", int VALUE}
|
||||
{"option3",2}
|
||||
}
|
||||
};
|
||||
```
|
||||
Note that the number following the "option_x" string value, should be the value that you could retrieve from the optionField with the function `my_optionsField.selected_index_value();`
|
||||
|
||||
#### NumberField
|
||||
|
||||
@ -555,439 +316,6 @@ NewAppView::NewAppView(NavigationView &nav) {
|
||||
}
|
||||
```
|
||||
|
||||
#### Waveform
|
||||
|
||||
Waveforms are used to display a sign wave from a signal source. **Note:** The X axes represents time while the Y axes represents amplitude. Here you can find it's declaration and prototype:
|
||||
```
|
||||
Labels my_waveform_widget{
|
||||
Rect parent_rect,
|
||||
int16_t * data,
|
||||
uint32_t length,
|
||||
int32_t offset,
|
||||
bool digital,
|
||||
Color color
|
||||
};
|
||||
```
|
||||
|
||||
For example, let's say you want a Waveform called `my_waveform`. You will need to add this to `apps/ui_newapp.hpp`:
|
||||
```
|
||||
Waveform my_waveform(
|
||||
{0, 5*16, 240, 64}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
|
||||
waveform_buffer, // RX data
|
||||
128, // Length, how many elements in the waveform_buffer array
|
||||
0, // Offset
|
||||
false, // Digital
|
||||
Color::white() // Sine wave color
|
||||
);
|
||||
```
|
||||
**Note:** Colors are defined in [`firmware/common/ui.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/ui.hpp).
|
||||
|
||||
|
||||
The data being displayed by `my_waveform` needs to be a `int16_t` array. You can declare this variable in `apps/ui_newapp.hpp`:
|
||||
```
|
||||
class NewAppView : public View
|
||||
{
|
||||
public:
|
||||
|
||||
...
|
||||
|
||||
private:
|
||||
int16_t waveform_buffer[128]; // Data for Waveform
|
||||
|
||||
...
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
In `apps/ui_newapp.cpp` you'll need to add the `my_waveform` pointer to add_child() or add_children():
|
||||
```
|
||||
NewAppView::NewAppView(NavigationView &nav) {
|
||||
|
||||
// Widget pointers
|
||||
add_children({
|
||||
&my_waveform,
|
||||
});
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
If your input data has a variable length you can use the `set_length(const uint32_t new_length)` function to update the Waveform:
|
||||
```
|
||||
my_waveform.set_length(9001) // THAT'S OVER 18KB!!
|
||||
```
|
||||
|
||||
#### VuMeter
|
||||
|
||||
VuMeters are used to visually represent the sound intensity of an audio source. Here you can find it's declaration and prototype:
|
||||
```
|
||||
Labels my_vuMeter_widget{
|
||||
Rect parent_rect,
|
||||
uint32_t LEDs,
|
||||
bool show_max
|
||||
};
|
||||
```
|
||||
|
||||
For example, let's say you want a VuMeter called `my_vuMeter`. You will need to add this to `apps/ui_newapp.hpp`:
|
||||
```
|
||||
VuMeter my_vuMeter(
|
||||
{ 0*8, 1*8, 2*8, 33*8}, // Coordinates are: int:x (px), int:y (px), int:width (px), int:height (px)
|
||||
12, // LEDs
|
||||
true // Show max
|
||||
);
|
||||
```
|
||||
|
||||
In `apps/ui_newapp.cpp` you'll need to add the `my_vuMeter` pointer to add_child() or add_children():
|
||||
```
|
||||
NewAppView::NewAppView(NavigationView &nav) {
|
||||
|
||||
// Widget pointers
|
||||
add_children({
|
||||
&my_vuMeter,
|
||||
});
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
To set the value for `my_vuMeter` use the `set_value(const uint32_t new_value)` function:
|
||||
```
|
||||
my_vuMeter.set_value(123); // Max is 255
|
||||
```
|
||||
|
||||
## Access radio hardware
|
||||
|
||||
Now we’re going to dive into something a little more complex and bring radios into the mix. If you’re not familiar with the LPC43xx please read up on the [Firmware Architecture](https://github.com/eried/portapack-mayhem/wiki/Firmware-Architecture) before continuing.
|
||||
|
||||
So far we’ve only been dealing with application code with is ran on the M0 of the LPC43xx. Now we’re going to start working with the baseband side of the codebase which is ran on the LPC43xx’s M4 processor. Both of these processors use 8k worth of shared memory from `0x1008_8000` to `0x1008_a000` to pass messages to and from each other. The M0 controls **ALL** operations of the portapack while the M4 mostly handles the DSP and radio functions.
|
||||
|
||||
Complexitys aside with the two processors, accessing the HackRF's radio hardware has been simplified with helper classes such as the [`TransmitterModel`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/transmitter_model.cpp) and [`ReceiverModel`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/receiver_model.cpp). Both of these classes interface with the M4 baseband processes and gives us a more piratical way to control the radio.
|
||||
|
||||
Other classes and structs such as [`baseband api`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/baseband_api.cpp) and [`SharedMemory`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/portapack_shared_memory.hpp) also bridge the gap between the M0 and M4. Even though the M4's primary responsability is to handle DSP with the radio hardware the M0 can still be used to decode data. For example, classes found in `firmware/application/protocols/` like [`encoders`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/protocols/encoders.cpp) still send data too and from the two processors but also encodes and decodes messages at the higher level protocols.
|
||||
|
||||
|
||||
### TX
|
||||
|
||||
The code bellow is an example OOK TX application using [`TransmitterModel`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/transmitter_model.cpp), [`encoders`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/protocols/encoders.cpp), and [`baseband`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/baseband_api.cpp).
|
||||
|
||||
#### ui_newapp.hpp
|
||||
|
||||
....
|
||||
|
||||
// Include TransmitterModel
|
||||
#include "transmitter_model.hpp"
|
||||
|
||||
namespace ui
|
||||
{
|
||||
class NewAppView : public View // App class declaration
|
||||
{
|
||||
public:
|
||||
|
||||
....
|
||||
|
||||
private:
|
||||
|
||||
....
|
||||
|
||||
void start_tx(std::string& message); // Function declarations
|
||||
void stop_tx();
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_progress { // MessageHandlerRegistration class which relays
|
||||
Message::ID::TXProgress, // Message::ID::TXProgress messages to your app
|
||||
[this](const Message* const p) { // code from baseband. The Ternary Operator passes
|
||||
const auto message = *reinterpret_cast<const TXProgressMessage*>(p); // an uint32_t progressvalue and a bool stating if
|
||||
this->on_tx_progress(message.progress, message.done); // TX progress has be complete.
|
||||
}};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#### ui_newapp.cpp
|
||||
|
||||
....
|
||||
|
||||
// Include encoders and baseband
|
||||
#include "encoders.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void NewAppView::start_tx(std::string& message) // Message input as "101101"
|
||||
{
|
||||
size_t bitstream_length = make_bitstream(message); // Function from encoders.hpp. Encodes then
|
||||
// sets message to TX data pointer via...
|
||||
// uint8_t * bitstream = shared_memory.bb_data.data;
|
||||
// on line 34 of encoders.cpp and returns length.
|
||||
|
||||
transmitter_model.set_tuning_frequency(433920000); // Center frequency in hz
|
||||
transmitter_model.set_sampling_rate(OOK_SAMPLERATE); // (2280000) Value from encoders.hpp
|
||||
transmitter_model.set_rf_amp(true); // RF amp on
|
||||
transmitter_model.set_baseband_bandwidth(1750000); // Bandwidth
|
||||
transmitter_model.enable(); // Radio enable
|
||||
|
||||
baseband::set_ook_data( // ASK/OOK TX function
|
||||
bitstream_length, // Length of message
|
||||
OOK_SAMPLERATE / 1766, // Symble period (560us), Sample Rate / Baud
|
||||
4, // Repeat transmissions
|
||||
100 // Pause symbles
|
||||
);
|
||||
}
|
||||
|
||||
void NewAppView::stop_tx() // Stop TX function
|
||||
{
|
||||
transmitter_model.disable(); // Disable transmitter_model
|
||||
|
||||
// Add UI logic to let the user know the TX has stoped
|
||||
}
|
||||
|
||||
NewAppView::NewAppView(NavigationView &nav) // Application Main
|
||||
{
|
||||
baseband::run_image(portapack::spi_flash::image_tag_ook); // M4 processor is being told to run proc_ook.cpp
|
||||
// found in the firmware/baseband/ folder. M4 is
|
||||
// then reset after this command.
|
||||
// UI widget logic and calls to
|
||||
// start_tx() goes here.
|
||||
|
||||
}
|
||||
|
||||
void NewAppView::on_tx_progress(const uint32_t progress, const bool done) // Function logic for when the message handler
|
||||
{ // sends a TXProgressMessage.
|
||||
if(done) {
|
||||
stop_tx();
|
||||
} else {
|
||||
// UI logic, update ProgressBar with progress var
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
### RX
|
||||
|
||||
Building from the example code for TX lets talk about how the baseband processes are started on the M4. The application code on the M0 uses the baseband api `baseband::run_image` to tell the M4 to run a process. The baseband images are defined in [`spi_image.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/spi_image.hpp) as the struct `image_tag_t`. These structs and have a 4 char array tag being used as an ID. Below is an example `image_tag_t` for AFSK RX.
|
||||
|
||||
```
|
||||
constexpr image_tag_t image_tag_afsk_rx { 'P', 'A', 'F', 'R' };
|
||||
```
|
||||
|
||||
Under [`firmware/baseband/CMakeLists.txt`](https://github.com/eried/portapack-mayhem/blob/next/firmware/baseband/CMakeLists.txt) the following code snippet shows how the baseband processes are linked to the images defined in [`spi_image.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/spi_image.hpp).
|
||||
|
||||
```
|
||||
### AFSK RX
|
||||
set(MODE_CPPSRC
|
||||
proc_afskrx.cpp
|
||||
)
|
||||
DeclareTargets(PAFR afskrx)
|
||||
```
|
||||
|
||||
In `firmware/baseband`, process or "proc" code for the M4 processor like [`proc_afskrx.cpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/baseband/proc_afskrx.cpp) for example can be found here. These proc classes are ran by [`BasebandThread`](https://github.com/eried/portapack-mayhem/blob/next/firmware/baseband/baseband_thread.cpp). All proc classes inherent [`BasebandProcessor`](https://github.com/eried/portapack-mayhem/blob/next/firmware/baseband/baseband_processor.hpp) and must include the parent functions.
|
||||
|
||||
#### baseband_processor.hpp
|
||||
```
|
||||
#ifndef __BASEBAND_PROCESSOR_H__
|
||||
#define __BASEBAND_PROCESSOR_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "channel_stats_collector.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
class BasebandProcessor {
|
||||
public:
|
||||
virtual ~BasebandProcessor() = default; // Constructor
|
||||
|
||||
virtual void execute(const buffer_c8_t& buffer) = 0; // DSP code for TX/RX, shared_memory messages can be sent to
|
||||
// M0 application code from this function.
|
||||
|
||||
virtual void on_message(const Message* const) { }; // Shared_memory messages from M0 application code
|
||||
|
||||
protected:
|
||||
void feed_channel_stats(const buffer_c16_t& channel);
|
||||
|
||||
private:
|
||||
ChannelStatsCollector channel_stats { };
|
||||
};
|
||||
```
|
||||
|
||||
Now that we have a better idea how M0 can drive the M4 lets talk about the Messaging between the two processors. The [`Message`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/message.hpp) class found under `firmware/common/`. Common code is used both by application (M0) and baseband (M4). Messages are handled by EventDispatcher found in [`event_m4.cpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/baseband/event_m4.cpp) for the baseband code and [`event_m0.cpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/event_m0.cpp) for the application code. Within the same file [`firmware/commen/message.hpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/message.hpp) you can find definitions for spacific message classes and ID. Bellow is an example message class for AFSK RX.
|
||||
|
||||
|
||||
#### message.hpp
|
||||
```
|
||||
class Message {
|
||||
public:
|
||||
static constexpr size_t MAX_SIZE = 512;
|
||||
|
||||
enum class ID : uint32_t {
|
||||
/* Assign consecutive IDs. IDs are used to index array. */
|
||||
|
||||
....
|
||||
|
||||
AFSKRxConfigure = 22,
|
||||
AFSKData = 47,
|
||||
|
||||
....
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
....
|
||||
|
||||
// Application Messages (M0) -> Baseband (M4)
|
||||
class AFSKRxConfigureMessage : public Message {
|
||||
public:
|
||||
constexpr AFSKRxConfigureMessage(
|
||||
const uint32_t baudrate,
|
||||
const uint32_t word_length,
|
||||
const uint32_t trigger_value,
|
||||
const bool trigger_word
|
||||
) : Message { ID::AFSKRxConfigure },
|
||||
baudrate(baudrate),
|
||||
word_length(word_length),
|
||||
trigger_value(trigger_value),
|
||||
trigger_word(trigger_word)
|
||||
{
|
||||
}
|
||||
|
||||
const uint32_t baudrate;
|
||||
const uint32_t word_length;
|
||||
const uint32_t trigger_value;
|
||||
const bool trigger_word;
|
||||
};
|
||||
|
||||
|
||||
// Baseband Messages (M4) -> Application (M0)
|
||||
class AFSKDataMessage : public Message {
|
||||
public:
|
||||
constexpr AFSKDataMessage(
|
||||
const bool is_data,
|
||||
const uint32_t value
|
||||
) : Message { ID::AFSKData },
|
||||
is_data { is_data },
|
||||
value { value }
|
||||
{
|
||||
}
|
||||
|
||||
bool is_data;
|
||||
uint32_t value;
|
||||
};
|
||||
```
|
||||
|
||||
[`SharedMemory`](https://github.com/eried/portapack-mayhem/blob/next/firmware/common/portapack_shared_memory.hpp) found in `firmware/common/` is used to pass data inbetween the application code (M0) to the baseband code (M4). Below is an example from [`proc_afskrx.cpp`](https://github.com/eried/portapack-mayhem/blob/next/firmware/baseband/proc_afskrx.cpp) on how data is sent back to the application [`AFSKRxView`](https://github.com/eried/portapack-mayhem/blob/next/firmware/application/apps/ui_afsk_rx.cpp).
|
||||
|
||||
#### proc_afskrx.cpp
|
||||
```
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
void AFSKRxProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
.... // RX Logic
|
||||
|
||||
shared_memory.application_queue.push(data_message); // data_message is an AFSKDataMessage object
|
||||
|
||||
.... // MORE RX Logic
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
Continuing to use the same AFSK RX proc code above, below is an example of an RX AFSK application.
|
||||
|
||||
|
||||
#### ui_newapp.hpp
|
||||
|
||||
....
|
||||
|
||||
// Include ReceiverModel
|
||||
#include "receiver_model.hpp"
|
||||
|
||||
namespace ui
|
||||
{
|
||||
class NewAppView : public View // App class declaration
|
||||
{
|
||||
public:
|
||||
|
||||
....
|
||||
|
||||
private:
|
||||
|
||||
....
|
||||
|
||||
void start_rx(); // Function declarations
|
||||
void stop_rx();
|
||||
void on_data();
|
||||
MessageHandlerRegistration message_handler_packet { // MessageHandlerRegistration class which relays
|
||||
Message::ID::AFSKData, // relays messages to your app code from baseband.
|
||||
[this](Message* const p) { // Every time you get a AFSKData message the
|
||||
const auto message = static_cast<const AFSKDataMessage*>(p); // on_data() function will be triggered.
|
||||
this->on_data(message->value, message->is_data);
|
||||
}};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#### ui_newapp.cpp
|
||||
|
||||
....
|
||||
|
||||
#include "modems.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace modems;
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void NewAppView::start_rx() // Start RX function
|
||||
{
|
||||
auto def_bell202 = &modem_defs[0]; // Bell202 baud rate
|
||||
persistent_memory::set_modem_baudrate(def_bell202->baudrate); // Set RX modem to 1200 baud
|
||||
|
||||
serial_format_t serial_format; // Declare packet format for message
|
||||
serial_format.data_bits = 7; // Bit length
|
||||
serial_format.parity = EVEN; // Even or odd parity bit
|
||||
serial_format.stop_bits = 1; // Stop bit
|
||||
serial_format.bit_order = LSB_FIRST; // LSB or MSB first
|
||||
persistent_memory::set_serial_format(serial_format); // Set RX packet format
|
||||
|
||||
baseband::set_afsk(persistent_memory::modem_baudrate(), 8, 0, false); // Baud rate, word length, trigger value, trigger word
|
||||
|
||||
receiver_model.set_tuning_frequency(433920000); // Center frequency in hz
|
||||
receiver_model.set_sampling_rate(3072000); // Sampling rate
|
||||
receiver_model.set_baseband_bandwidth(1750000); // Bandwidth
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); // Modulation
|
||||
receiver_model.enable(); // Start RX
|
||||
|
||||
audio::set_rate(audio::Rate::Hz_24000); // Play RX audio to headphone jack
|
||||
audio::output::start();
|
||||
}
|
||||
|
||||
void NewAppView::stop_rx() // Stop RX function
|
||||
{
|
||||
audio::output::stop(); // Stop Audio
|
||||
receiver_model.disable(); // Stop RX
|
||||
baseband::shutdown(); // Stop M4 proc process
|
||||
}
|
||||
|
||||
NewAppView::NewAppView(NavigationView &nav) // Application Main
|
||||
{
|
||||
baseband::run_image(portapack::spi_flash::image_tag_afsk_rx); // M4 processor is being told to run proc_afskrx.cpp
|
||||
// found in the firmware/baseband/ folder. M4 is
|
||||
// then reset after this command.
|
||||
// UI widget logic and calls to start_rx()
|
||||
// and stop_rx() goes here.
|
||||
|
||||
}
|
||||
|
||||
void NewAppView::on_data(uint32_t value, bool is_data) // Function logic for when the message handler
|
||||
{ // sends a AFSKData.
|
||||
if(is_data) {
|
||||
// RX data handling Logic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Wrap up
|
||||
|
||||
Bellow is an example "Hello World" application that shows off a few widgets and logic that controls their functions.
|
||||
|
Loading…
x
Reference in New Issue
Block a user