Skip to content

Add SoftwareSerial library #32

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
4 changes: 2 additions & 2 deletions .github/workflows/build_examples.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ jobs:
- name: Generate build matrix
id: gen_matrix
run: |
# Get all subdirectories containing platformio.ini in the ./example directory
subdirectories=$(find ./examples -type f -name "platformio.ini" -exec dirname {} \; | sort -u)
# Get all subdirectories containing platformio.ini in the ./examples and ./libraries/*/examples directories
subdirectories=$(find ./examples ./libraries/*/examples -type f -name "platformio.ini" -exec dirname {} \; | sort -u)

# Convert subdirectories to JSON array
json_array="["
Expand Down
2 changes: 1 addition & 1 deletion cores/arduino/drivers/interrupts/interrupts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ en_result_t _irqn_aa_resign(IRQn_Type &irqn)
{
// do nothing since resigning the interrupt already frees the IRQn
// only check that the interrupt was actually resigned before calling this function
CORE_ASSERT(ram_vector_table.irqs[irqn] != no_handler, "IRQ was not resigned before auto-assign resignment",
CORE_ASSERT(ram_vector_table.irqs[irqn] == no_handler, "IRQ was not resigned before auto-assign resignment",
return Error);
return Ok;
}
2 changes: 2 additions & 0 deletions libraries/SPI/src/SPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <drivers/gpio/gpio.h>
#include <drivers/sysclock/sysclock.h>

#warning "SPI on the HC32F460 has not been tested yet. See https://github.com/shadow578/framework-arduino-hc32f46x/pull/29"

/**
* @brief given a integer v, round up to the next power of two
* @note based on https://stackoverflow.com/a/466242
Expand Down
2 changes: 0 additions & 2 deletions libraries/SPI/src/SPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
#error "SPI library requires PWC DDL to be enabled"
#endif

#warning "SPI on the HC32F460 has not been tested yet. See https://github.com/shadow578/framework-arduino-hc32f46x/pull/29"


// SPI_HAS_TRANSACTION means SPI has
// - beginTransaction()
Expand Down
92 changes: 92 additions & 0 deletions libraries/SoftwareSerial/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Software Serial for the HC32F460

The Software Serial library allows serial (UART) communication on any digital pin of the board, bit-banging the protocol.
It it possible to have multiple software serial ports.

The implementation of this library is based on the [SoftwareSerial library of the STM32duino project](https://github.com/stm32duino/Arduino_Core_STM32/blob/main/libraries/SoftwareSerial/).


## Configuration Options

To configure the library, you may add the following defines to your build environment.

| Name | Default | Description |
|-|-|-|
| `SOFTWARE_SERIAL_BUFFER_SIZE` | `32` | size of the receive buffer. it's highly likely that any transmission longer than this will be partially lost. |
| `SOFTWARE_SERIAL_OVERSAMPLE` | `3` | oversampling rate. Each bit period is equal to OVERSAMPLE ticks, and bits are sampled in the middle |
| `SOFTWARE_SERIAL_HALF_DUPLEX_SWITCH_DELAY` | `5` | bit periods before half duplex switches TX to RX |
| `SOFTWARE_SERIAL_TIMER_PRESCALER` | `2` | prescaler of the TIMER0. set according to PCLK1 and desired baud rate range |
| `SOFTWARE_SERIAL_TIMER0_UNIT` | `TIMER01B_config` | TIMER0 unit to use for software serial. Using TIMER01A is not recommended |
| `SOFTWARE_SERIAL_TIMER_PRIORITY` | `3` | interrupt priority of the timer interrupt |
| `SOFTWARE_SERIAL_FLUSH_CLEARS_RX_BUFFER` | `SOFTWARE_SERIAL_STM32_API_COMPATIBILITY` | behaviour of the `flush()` method. `0` = waits for pending TX to complete. `1` = clear RX buffer. STMduino library uses behaviour `1` |
| `SOFTWARE_SERIAL_STM32_API_COMPATIBILITY` | `0` | compatibility with STM32duino library. `0` = sensible API. `1` = compatible with STM32duino API. |


> [!TIP]
> for existing projects that originated from STM32duino, you may set `SOFTWARE_SERIAL_STM32_API_COMPATIBILITY` to `1` to maintain compatibility with the STM32duino library.


### Calculating `SOFTWARE_SERIAL_TIMER_PRESCALER`

to calculate, use the following c++ program

```cpp
#include <iostream>
#include <stdint.h>

float get_real_frequency(const uint32_t frequency, const uint16_t prescaler)
{
const uint32_t base_frequency = 50000000; // 50 MHz PCLK1

// calculate the compare value needed to match the target frequency
// CMP = (base_freq / prescaler) / frequency
uint32_t compare = (base_frequency / uint32_t(prescaler)) / frequency;

if (compare <= 0 || compare > 0xFFFF)
{
return -1;
}

// calculate the real frequency
float real_frequency = (base_frequency / prescaler) / compare;
return real_frequency;
}


int main()
{
const uint32_t baud = 9600;
const uint32_t oversampling = 3;

const uint32_t frequency = baud * oversampling;
const uint16_t prescalers[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};

float min_error = 100000;
uint16_t best_prescaler = 0;
for (auto p : prescalers)
{
const float real_frequency = get_real_frequency(frequency, p);
const float error = std::abs(real_frequency - frequency);
if (error < min_error)
{
min_error = error;
best_prescaler = p;
}

std::cout << "Prescaler: " << static_cast<int>(p)
<< ", Real frequency: " << real_frequency
<< ", Error: " << error
<< std::endl;
}

float best_real_frequency = get_real_frequency(frequency, best_prescaler);
float best_baud = best_real_frequency / oversampling;
float baud_percent_error = (best_baud - baud) / baud * 100;
std::cout << "Best prescaler: " << static_cast<int>(best_prescaler)
<< ", Real frequency: " << best_real_frequency
<< ", Error: " << min_error
<< ", Real baud rate: " << best_baud
<< ", Baud rate error: " << baud_percent_error << "%"
<< std::endl;
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.pio
.vscode
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[env]
platform = https://github.com/shadow578/platform-hc32f46x/archive/1.1.0.zip
framework = arduino
board = generic_hc32f460
build_flags =
-D USART_AUTO_CLKDIV_OS_CONFIG # enable auto clock division and oversampling configuration (recommended)
-D __DEBUG=1
-D __CORE_DEBUG=1

[env:default]

# required only for CI
[env:ci]
# override the framework-arduino-hc32f46x package with the local one
board_build.arduino_package_dir = ../../../../
extra_scripts =
pre:../../../../tools/ci/patch_get_package_dir.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Software serial basic example.
*
* echos any character received on the software serial port back to the sender.
* also allows to change the baud rate by sending a number from 1 to 8.
*
* without changing the system clock, the maximum baud rate is around 9600 baud.
*/
#include <Arduino.h>
#include <SoftwareSerial.h>

constexpr gpio_pin_t RX_PIN = PA15;
constexpr gpio_pin_t TX_PIN = PA9;

SoftwareSerial mySerial(/* RX */ RX_PIN, /* TX */ TX_PIN);

void setup()
{
mySerial.begin(9600);
mySerial.println("Hello, world!");
}

void loop()
{
while (mySerial.available())
{
const char c = mySerial.read();
mySerial.write(c);

uint32_t new_baud = 0;
switch(c)
{
case '1': new_baud = 1200; break;
case '2': new_baud = 2400; break;
case '3': new_baud = 4800; break;
case '4': new_baud = 9600; break;
case '5': new_baud = 19200; break;
case '6': new_baud = 38400; break;
case '7': new_baud = 57600; break;
case '8': new_baud = 115200; break;
default: break;
}
if (new_baud != 0)
{
mySerial.print("Set baud to:");
mySerial.print(new_baud);
delay(100);
mySerial.begin(new_baud);
mySerial.println(" - Done");
}
}

delay(10);
}
Loading