kopia lustrzana https://github.com/mikaelnousiainen/RS41ng
Graw DFM-17 radiosonde support (#61)
* Initial plumbing for DFM17 (#54) * Implementing Si4063 radio chip support for DFM17 sonde. Work in progress. * Add an error to indicate that the code does not work yet * Removed copyrighted docs from documentation and provided links instead (#55) * Fix SPI bus initialization for DFM17. Trying to make Si4063 SPI communication work (in progress). Fix LED bit polarity. * Set DFM17 clocks correctly to 24 MHz * Still trying to fix Si4063 SPI communication, no luck * Add clearing of SPI overrun status flag to make Si4063 comm work properly in DFM17. Not sure about the reason this is required. * Fix bug when setting Si4063 frequency. Remove unnecessary GPS debug and delays. Add Si4063 debug printout. Find a suitable frequency offset multiplier to achieve 270 Hz tone spacing for Horus 4FSK. * Add some known good Si4063 configuration values. Implement direct GPIO-based modulation for Si4063 that allows support for CW now. * DFM-17 APRS work (#60) * Modify for APRS on DFM17 * Clean up Si4063 APRS work and make APRS deviation configurable * Add documentation for DFM-17 * Add a note about the DFM-17 connector * More work on docs and config for DFM-17 * Docs * Mor work on config * Fix RS41 URL * Added info about high-altitude balloon flights * Fix typo * More code cleanup * Update authors * Improve DFM-17 docs * Added a timepulse routine for DFM17 and also a millis() routine. Working toward clock calibration. * More refinements to the HCI calibration * Cleaned up clock calibration code and integrated into the main radio loop. Also added APRS telemetry for calibration. * Added logic to (hopefully) avoid over-calibration if there is an errant timepulse. * Clean up DFM-17 clock calibration implementation and make RS41 compile properly * Adjust README * Add notes about DFM-17 clock calibration * Fix typo * Add DFM-17 note about clock calibration to the top of the README * Text style * Working on README * Working on README * Move datasheet links to main README * Markdown styling * Improve config file structure * Remove unnecessary comments --------- Co-authored-by: Mike Hojnowski <kd2eat@gmail.com>pull/79/head
rodzic
18b6e1ffb4
commit
76e2fd1fae
233
README.md
233
README.md
|
@ -1,25 +1,37 @@
|
|||
# RS41ng - Amateur radio firmware for Vaisala RS41 radiosonde
|
||||
# RS41ng - Amateur radio firmware for Vaisala RS41 and Graw DFM-17 radiosondes
|
||||
|
||||
**NOTE:** While this firmware has been tested with great success on a number of high-altitude balloon
|
||||
**NEW:** Experimental support for Graw DFM-17 radiosondes added! Please test and report any issues!
|
||||
|
||||
**NOTE:** **DFM-17 radiosondes require a GPS lock (and clear visibility to the sky) to calibrate its internal oscillator.**
|
||||
DFM-17 transmissions, especially APRS, may not decode correctly because of incorrect timing before the internal oscillator has been calibrated.
|
||||
|
||||
**NOTE:** While this firmware has been tested (on RS41) with great success on a number of high-altitude balloon
|
||||
flights, it is still a work in progress and some features might not work as expected yet!
|
||||
In particular, the time sync (scheduling) features and use of an external Si5351 as a transmitter need more testing.
|
||||
|
||||
This is a custom, amateur radio-oriented firmware for [Vaisala RS41 radiosondes](https://www.vaisala.com/en/products/instruments-sensors-and-other-measurement-devices/soundings-products/rs41).
|
||||
## What is RS41ng?
|
||||
|
||||
RS41ng is a custom, amateur radio-oriented firmware for [Vaisala RS41](https://www.vaisala.com/en/products/weather-environmental-sensors/upper-air-radiosondes-rs41-rs41-e-models)
|
||||
and [Graw DFM-17](https://www.graw.de/products/radiosondes/dfm-17/) radiosondes. These radiosonde models
|
||||
have very similar hardware, so that it is relatively easy to support both with the same codebase.
|
||||
It is unlikely that RS41ng could support any other radiosonde hardware for now.
|
||||
|
||||
Some code is based on an earlier RS41 firmware project called [RS41HUP](https://github.com/df8oe/RS41HUP),
|
||||
but most of it has been rewritten from scratch. The Horus 4FSK code has been adapted from
|
||||
the [darksidelemm fork of RS41HUP](https://github.com/darksidelemm/RS41HUP).
|
||||
|
||||
## Asking questions, filing feature requests and reporting issues
|
||||
|
||||
* Please use [GitHub discussions](../../discussions) for asking questions and for sharing info about your RS41-based projects
|
||||
* Please use [GitHub discussions](../../discussions) for asking questions and for sharing info about your radiosonde-based projects
|
||||
* For example, questions about firmware configuration and connecting of external chips to the sonde belong here
|
||||
* Please use [GitHub issues](../../issues) to file new feature requests or issues that you have already identified with the firmware
|
||||
* However, please remember to post questions about usage to [GitHub discussions](../../discussions)
|
||||
|
||||
## What are the Vaisala RS41 radiosondes and how can I get one?
|
||||
## What are radiosondes and how can I get one?
|
||||
|
||||
The RS41 radiosondes are used extensively for atmospheric sounding by the meteorological institutes in various countries and thus easily
|
||||
available to be collected once they land, an activity called radiosonde hunting: see YouTube presentation about
|
||||
Radiosondes, especially the RS41 and DFM-17, are used extensively for atmospheric sounding by the meteorological
|
||||
institutes in various countries and thus easily available to be collected once they land, an activity called
|
||||
radiosonde hunting: see YouTube presentation about
|
||||
[Tracking and Chasing Weather Balloons by Andreas Spiess](https://www.youtube.com/watch?v=vQfztG60umI) or
|
||||
[Chasing Radiosonde Weather Balloons used in Meteorology for Fun by Mark VK5QI](https://www.youtube.com/watch?v=fb9gNomWrAY)
|
||||
for more details!
|
||||
|
@ -34,23 +46,44 @@ For your own receiver station, you will need:
|
|||
3. Radiosonde tracker software: common choices are [RS41 Tracker](http://escursioni.altervista.org/Radiosonde/) for Windows
|
||||
and [radiosonde_auto_rx](https://github.com/projecthorus/radiosonde_auto_rx) for Linux / Raspberry Pi.
|
||||
|
||||
### What can I do with an RS41 radiosonde?
|
||||
### What can I do with the RS41 and DFM-17 radiosondes?
|
||||
|
||||
The [Vaisala RS41 radiosondes](https://www.vaisala.com/en/products/instruments-sensors-and-other-measurement-devices/soundings-products/rs41)
|
||||
uses an off-the-shelf [STM32F100C8](https://www.st.com/en/microcontrollers-microprocessors/stm32f100c8.html)
|
||||
32-bit microcontroller, which can be reprogrammed using an [ST-LINK v2 programmer](https://www.st.com/en/development-tools/st-link-v2.html)
|
||||
The [Vaisala RS41](https://www.vaisala.com/en/products/weather-environmental-sensors/upper-air-radiosondes-rs41-rs41-e-models)
|
||||
and [Graw DFM-17](https://www.graw.de/products/radiosondes/dfm-17/)
|
||||
radiosondes both use off-the-shelf 32-bit [STM32F100-series](https://www.st.com/en/microcontrollers-microprocessors/stm32f100-value-line.html) microcontrollers,
|
||||
which can be reprogrammed using an [ST-LINK v2 programmer](https://www.st.com/en/development-tools/st-link-v2.html)
|
||||
or a smaller [ST-LINK v2 USB dongle](https://www.adafruit.com/product/2548).
|
||||
|
||||
The RS41 hardware can be programmed to transmit different kinds of RF modulations (morse code, APRS and different FSK modulations)
|
||||
on the 70 cm (~433 MHz) amateur radio band. The radiosonde contains a [UBX-G6010](https://www.u-blox.com/en/product/ubx-g6010-st-chip)
|
||||
GPS chip, so it can be used as a tracker device, e.g. for high-altitude balloons.
|
||||
There is detailed information about the hardware of these radiosonde models on the following pages:
|
||||
|
||||
The RS41ng firmware is just one example of what can be achieved with the RS41 hardware!
|
||||
* https://github.com/bazjo/RS41_Hardware
|
||||
* https://wiki.recessim.com/view/DFM-17_Radiosonde
|
||||
|
||||
The radiosondes can be reprogrammed to transmit different kinds of RF modulations (morse code, APRS and different FSK modulations)
|
||||
on the 70 cm (~433 MHz) amateur radio band. The radiosonde contain Ublox
|
||||
GPS chips, so it can be used as a tracker device, e.g. for high-altitude balloons.
|
||||
|
||||
The RS41ng firmware is just one example of what can be achieved with the hardware of these radiosondes!
|
||||
|
||||
## What are high-altitude balloon flights?
|
||||
|
||||
High-altitude balloon flights arranged by hobbyists are fun technical experiments.
|
||||
The flight goals are usually related to aerial photography, testing of radio tracker and transmitter hardware
|
||||
and different kinds of measurements with on-board sensors.
|
||||
|
||||
The following websites contain more information about planning and launching a high-altitude balloon flight:
|
||||
|
||||
* https://www.overlookhorizon.com/ - Overlook Horizon high-altitude balloon flights.
|
||||
* http://www.projecthorus.org/ - Australian high-altitude balloon project. Website no longer updated.
|
||||
* https://www.areg.org.au/archives/category/activities/project-horus - Newer Project Horus flights.
|
||||
* https://ukhas.org.uk/ - UK high-altitude society.
|
||||
* http://www.daveakerman.com/ - High-altitude balloon blog of Dave Akerman.
|
||||
* https://0xfeed.tech/ - High-altitude balloon / ham radio blog of Mikael Nousiainen OH3BHX.
|
||||
|
||||
## Why does the RS41ng firmware exist?
|
||||
|
||||
The motivation to develop this firmware is to provide a clean, customizable and
|
||||
modular codebase for developing RS41 radiosonde-based experiments.
|
||||
modular codebase for developing radiosonde-based experiments.
|
||||
|
||||
See the feature list below.
|
||||
|
||||
|
@ -60,25 +93,37 @@ The main features the RS41ng firmware are:
|
|||
|
||||
* Support for multiple transmission modes:
|
||||
* Standard 1200-baud APRS
|
||||
* Option to transmit APRS weather reports using readings from an external BMP280/BME280 sensor
|
||||
* Option to transmit APRS weather reports using readings from an external BMP280/BME280 sensor (only RS41 supports custom sensors)
|
||||
* [Horus 4FSK v1 and v2 modes](https://github.com/projecthorus/horusdemodlib/wiki) that has improved performance compared to APRS or RTTY
|
||||
* There is an option to use continuous transmit mode (for either V1 or V2 mode), which helps with receiver frequency synchronization and improves reception.
|
||||
* In order to use Horus 4FSK mode on a flight, you will need to request a new Horus 4FSK payload ID in GitHub according to the instructions at: https://github.com/projecthorus/horusdemodlib/wiki#how-do-i-transmit-it
|
||||
* Morse code (CW)
|
||||
* JT65/JT9/JT4/FT8/WSPR/FSQ digital modes on HF/VHF amateur radio bands using an external Si5351 clock generator connected to the external I²C bus
|
||||
* "Pip" mode, which transmits a short beep generated using CW to indicate presence of the transmitter
|
||||
* **RS41 only:** JT65/JT9/JT4/FT8/WSPR/FSQ digital modes on HF/VHF amateur radio bands using an external Si5351 clock generator connected to the external I²C bus
|
||||
* Support for transmitting multiple modes consecutively with custom, rotating comment messages (see `config.c`)
|
||||
* Support for GPS-based scheduling is available for transmission modes that require specific timing for transmissions
|
||||
* Enhanced support for the internal Si4032 radio transmitter via PWM-based tone generation
|
||||
* Extensibility to allow easy addition of new transmission modes and new sensors
|
||||
|
||||
Features available on RS41 hardware only:
|
||||
|
||||
* Support for custom sensors via the external I²C bus
|
||||
* Support for counting pulses on expansion header pin 2 (I2C2_SDA (PB11) / UART3 RX) for use with sensors like Geiger counters
|
||||
* GPS NMEA data output via the external serial port pin 3 (see below). This disables use of I²C devices as the serial port pins are shared with the I²C bus pins.
|
||||
* This allows using the RS41 sonde GPS data in external tracker hardware, such as Raspberry Pi or other microcontrollers.
|
||||
* Enhanced support for the internal Si4032 radio transmitter via PWM-based tone generation (and ultimately DMA-based symbol timing, if possible)
|
||||
* Extensibility to allow easy addition of new transmission modes and new sensors
|
||||
|
||||
Notes for DFM-17:
|
||||
|
||||
* **DFM-17 radiosondes require a GPS lock (and clear visibility to the sky) to calibrate its internal oscillator.**
|
||||
This is necessary, because the internal oscillator is not particularly accurate.
|
||||
DFM-17 transmissions, especially APRS, may not decode correctly because of incorrect timing before
|
||||
the internal oscillator has been calibrated.
|
||||
* The RS41 radiosonde hardware uses an external oscillator, which is more stable, so RS41 does not
|
||||
suffer from the same issue.
|
||||
|
||||
### Transmission modes
|
||||
|
||||
On the internal Si4032 transmitter:
|
||||
On the internal Si4032 (RS41) and Si4063 (DFM-17) transmitters:
|
||||
|
||||
* APRS (1200 baud)
|
||||
* Horus 4FSK v1 and v2 (100 baud)
|
||||
|
@ -96,7 +141,7 @@ On an external Si5351 clock generator connected to the external I²C bus of the
|
|||
#### Notes about APRS
|
||||
|
||||
* Bell 202 frequencies are generated via hardware PWM, but the symbol timing is created in a loop with delay
|
||||
* There is also code available to use DMA transfers for symbol timing to achieve greater accuracy, but I have not been able to get the timings working correctly
|
||||
* There is also code available to use DMA transfers for symbol timing to achieve greater accuracy, but the timings are not working correctly
|
||||
|
||||
#### Notes about Horus 4FSK
|
||||
|
||||
|
@ -105,7 +150,7 @@ On an external Si5351 clock generator connected to the external I²C bus of the
|
|||
* See [horus-gui installation and usage instructions](https://github.com/projecthorus/horusdemodlib/wiki/1.1-Horus-GUI-Reception-Guide-(Windows-Linux-OSX)) and [horusdemodlib](https://github.com/projecthorus/horusdemodlib) library that is responsible for demodulating the signal.
|
||||
* In order to use Horus 4FSK mode on a flight, you will need to request a new Horus 4FSK payload ID in GitHub according to the instructions at: https://github.com/projecthorus/horusdemodlib/wiki#how-do-i-transmit-it
|
||||
|
||||
### External sensors
|
||||
### External sensors (RS41 only)
|
||||
|
||||
It is possible to connect external sensors to the I²C bus of the RS41 radiosonde.
|
||||
|
||||
|
@ -123,7 +168,7 @@ Sensor driver code contributions are welcome!
|
|||
|
||||
* Configurable transmission frequencies and schedules based on location / altitude
|
||||
* Support for more I²C sensors
|
||||
* RTTY on both Si4032 (70 cm, non-standard shift) and Si5351 (HF + 2m) with configurable shift
|
||||
* RTTY on both Si4032/Si4063 (70 cm, non-standard shift) and Si5351 (HF + 2m) with configurable shift
|
||||
* Investigate possibility to implement 1200 bps Bell 202 modulation (and
|
||||
possibly also 300 bps Bell 103 modulation) for APRS using Si5351,
|
||||
this requires special handling to make Si5351 change frequency quickly
|
||||
|
@ -133,11 +178,20 @@ Sensor driver code contributions are welcome!
|
|||
|
||||
1. Configure your amateur radio call sign, transmission schedule (time sync),
|
||||
transmit frequencies and transmission mode parameters in `config.h`
|
||||
2. Set up transmitted message templates in `config.c`, depending on the modes you use
|
||||
* Select the desired radiosonde type in the beginning of the file by removing the `//` comment from either
|
||||
the `#define RS41` or `#define DFM17` lines.
|
||||
* Customize at least the following settings:
|
||||
* `CALLSIGN`
|
||||
* For RS41, the settings beginning with `RADIO_SI4032_` to select transmit power and the modes to transmit
|
||||
* For DFM-17, the settings beginning with `RADIO_SI4063_` to select transmit power and the modes to transmit
|
||||
* `HORUS_V2_PAYLOAD_ID` if you transmit Horus 4FSK
|
||||
* `APRS_COMMENT` if you transmit APRS
|
||||
2. Set up transmitted message templates in `config.c`, depending on the modes you use.
|
||||
You can customize the APRS and CW messages in more detail here.
|
||||
|
||||
### Power consumption and power-saving features
|
||||
|
||||
Power consumption notes (at 3V supply voltage) by Mark VK5QI:
|
||||
Power consumption notes (at 3V supply voltage) for RS41 by Mark VK5QI:
|
||||
|
||||
- GPS in full power acquisition mode: 110-120 mA (TX off), 160-170 mA (TX on)
|
||||
- GPS locked (5 sats), full power: 96 - 126 mA (TX off), 170 mA (TX on)
|
||||
|
@ -313,16 +367,21 @@ dnf install arm-none-eabi-gcc-cs arm-none-eabi-gcc-cs-c++ arm-none-eabi-binutils
|
|||
```
|
||||
3. The firmware will be stored in file `build/src/RS41ng.elf`
|
||||
|
||||
## Flashing the firmware
|
||||
## Prepare the radiosonde for flashing the firmware
|
||||
|
||||
Hardware requirements:
|
||||
|
||||
* A working [Vaisala RS41 radiosonde](https://www.vaisala.com/en/products/instruments-sensors-and-other-measurement-devices/soundings-products/rs41) :)
|
||||
* An [ST-LINK v2 programmer for the STM32 microcontroller](https://www.st.com/en/development-tools/st-link-v2.html) in the RS41 radiosonde.
|
||||
* These smaller [ST-LINK v2 USB dongles](https://www.adafruit.com/product/2548) also work well.
|
||||
* [Partco sells the ST-LINK v2 USB dongles in Finland](https://www.partco.fi/en/measurement/debugging/20140-stlinkv2.html)
|
||||
* A working [Vaisala RS41](https://www.vaisala.com/en/products/instruments-sensors-and-other-measurement-devices/soundings-products/rs41)
|
||||
or [Graw DFM-17](https://www.graw.de/products/radiosondes/dfm-17/) radiosonde :)
|
||||
* An [ST-LINK v2 programmer for the STM32 microcontroller](https://www.st.com/en/development-tools/st-link-v2.html) in the RS41 radiosonde.
|
||||
* These smaller [ST-LINK v2 USB dongles](https://www.adafruit.com/product/2548) also work well.
|
||||
* [Partco sells the ST-LINK v2 USB dongles in Finland](https://www.partco.fi/en/measurement/debugging/20140-stlinkv2.html)
|
||||
|
||||
The pinout of the RS41 connector (by DF8OE and VK5QI) is the following:
|
||||
Follow the instructions below for the radiosonde model you have.
|
||||
|
||||
### Vaisala RS41 programming connector
|
||||
|
||||
The pinout of the RS41 programming connector (by DF8OE and VK5QI) is the following:
|
||||
|
||||
```
|
||||
______________________| |______________________
|
||||
|
@ -352,22 +411,80 @@ ______________________| |______________________
|
|||
* 9 - SWDIO (PA13)
|
||||
* 10 - GND
|
||||
|
||||
### Steps to flash the firmware
|
||||
#### Connect the RS41 radiosonde to the programmer
|
||||
|
||||
1. If your ST-LINK v2 programmer is capable of providing power (as some third-party clones are),
|
||||
1. If your ST-LINK v2 programmer is capable of providing a voltage of 3.3V (as some third-party clones are),
|
||||
remove the batteries from the sonde. Otherwise, leave the batteries in and power on the sonde.
|
||||
2. Connect an ST-LINK v2 programmer dongle to the sonde via the following pins:
|
||||
* SWDIO -> Pin 9 (SWDIO)
|
||||
* SWCLK -> Pin 8 (SWCLK)
|
||||
* GND -> Pin 1 (GND)
|
||||
* 3.3V -> Pin 5 (MCU switch 3.3V) (only required when using the programmer to power the sonde)
|
||||
3. Unlock the flash protection - needed only before reprogramming the sonde for the first time
|
||||
* `openocd -f ./openocd_rs41.cfg -c "init; halt; flash protect 0 0 63 off; exit"`
|
||||
* **NOTE:** If the above command fails with an error about erasing sectors, try replacing the number `63` with `31`:
|
||||
* `openocd -f ./openocd_rs41.cfg -c "init; halt; flash protect 0 0 31 off; exit"`
|
||||
4. Flash the firmware
|
||||
* `openocd -f ./openocd_rs41.cfg -c "program build/src/RS41ng.elf verify reset exit"`
|
||||
5. Power cycle the sonde to start running the new firmware
|
||||
* SWDIO -> Pin 9 (SWDIO)
|
||||
* SWCLK -> Pin 8 (SWCLK)
|
||||
* GND -> Pin 1 (GND)
|
||||
* 3.3V -> Pin 5 (MCU switch 3.3V) (only required when using the programmer to power the sonde)
|
||||
|
||||
### Graw DFM-17 programming connector
|
||||
|
||||
The DFM-17 programming connector is an unpopulated group of pads on the circuit board
|
||||
between the sensor boom connector and the main STM32 microcontroller.
|
||||
|
||||
The pinout of the DFM-17 programming connector is the following:
|
||||
|
||||
```
|
||||
_____
|
||||
| |
|
||||
| S | _____________________
|
||||
| e | | |
|
||||
| n | | 1 2 |
|
||||
| s | | |
|
||||
| o | | 3 4 | ________________________
|
||||
| r | | | |
|
||||
| | | 5 6 | | STM32 microcontroller
|
||||
| b | | | |
|
||||
| o | | 7 8 | |
|
||||
| o | | | |
|
||||
| m | | 9 10 | |
|
||||
| | | | |
|
||||
| | |____________________ |
|
||||
| |
|
||||
|___|
|
||||
|
||||
(The sensor boom connector is on the left and the main microcontroller unit on the right side)
|
||||
```
|
||||
|
||||
* 1 - VTRef
|
||||
* This pin powers the device via 3.3V voltage from an ST-LINK programmer dongle
|
||||
* 2 - SWDIO / TMS
|
||||
* 3 - GND
|
||||
* 4 - SWCLK / TCK
|
||||
* 5 - GND
|
||||
* 6 - SWO EXT TRACECTL / TDO
|
||||
* 7 - KEY
|
||||
* 8 - NC EXT / TDI
|
||||
* 9 - GNDDetect
|
||||
* 10 - nRESET
|
||||
|
||||
#### Connect the DFM-17 radiosonde to the programmer
|
||||
|
||||
1. Since the DFM-17 programming connector is just an unpopulated group of pads on the circuit board,
|
||||
**you will need to either solder wires directly to the pads or alternatively solder a 0.05" (1.27mm) 5-by-2 pin header to the pads**.
|
||||
There are suitable ribbon cables with 5x2 0.05" connectors available for this type of pin header.
|
||||
2. Connect an ST-LINK v2 programmer dongle to the sonde via the following pins:
|
||||
* SWDIO -> Pin 2 (SWDIO)
|
||||
* SWCLK -> Pin 4 (SWCLK)
|
||||
* RST -> Pin 10 (RST)
|
||||
* GND -> Pin 5 (GND)
|
||||
* 3.3V -> Pin 1 (VTRef) (only required when using the programmer to power the sonde)
|
||||
3. If your ST-LINK v2 programmer is capable of providing a voltage of 3.3V (as some third-party clones are),
|
||||
remove the batteries from the sonde. Otherwise, leave the batteries in and power on the sonde.
|
||||
|
||||
## Flashing the radiosonde with the firmware (both RS41 and DFM-17)
|
||||
|
||||
1. Unlock the flash protection - needed only before reprogramming the sonde for the first time
|
||||
* `openocd -f ./openocd_rs41.cfg -c "init; halt; flash protect 0 0 63 off; exit"`
|
||||
* **NOTE:** If the above command fails with an error about erasing sectors, try replacing the number `63` with either `31` or the number the error message suggests:
|
||||
* `openocd -f ./openocd_rs41.cfg -c "init; halt; flash protect 0 0 31 off; exit"`
|
||||
2. Flash the firmware
|
||||
* `openocd -f ./openocd_rs41.cfg -c "program build/src/RS41ng.elf verify reset exit"`
|
||||
3. Power cycle the sonde to start running the new firmware
|
||||
|
||||
## Developing / debugging the firmware
|
||||
|
||||
|
@ -416,6 +533,8 @@ use command `file src/RS41ng.elf`.
|
|||
|
||||
## Si4032 Bell FSK modulation hack for APRS
|
||||
|
||||
Notes by Mikael OH3BHX:
|
||||
|
||||
The idea behind the APRS / Bell 202 modulation implementation is based on RS41HUP project and its "ancestors"
|
||||
and I'm describing it here, since it has not been documented elsewhere.
|
||||
|
||||
|
@ -483,7 +602,9 @@ rtl_fm -f 432500000 -M fm -s 250k -r 48000 -g 22 - | ./aprs -
|
|||
|
||||
# Authors
|
||||
|
||||
* Mikael Nousiainen OH3BHX <oh3bhx@sral.fi>
|
||||
* Mikael Nousiainen OH3BHX <mikael@0xfeed.tech>
|
||||
* Mike Hojnowski KD2EAT contributed significantly to Graw DFM-17 support
|
||||
* Various authors with smaller contributions from GitHub pull requests
|
||||
* Original codebase: DF8OE and other authors of the [RS41HUP](https://github.com/df8oe/RS41HUP) project
|
||||
* Horus 4FSK code adapted from [darksidelemm fork of RS41HUP](https://github.com/darksidelemm/RS41HUP) project
|
||||
|
||||
|
@ -496,8 +617,26 @@ rtl_fm -f 432500000 -M fm -s 250k -r 48000 -g 22 - | ./aprs -
|
|||
* http://happysat.nl/RS-41/RS41.html - Vaisala RS-41 SGP Modification and info about the original firmware settings
|
||||
* https://destevez.net/2018/06/flashing-a-vaisala-rs41-radiosonde/
|
||||
* https://destevez.net/2017/11/tracking-an-rs41-sgp-radiosonde-and-reporting-to-aprs/
|
||||
* https://github.com/digiampietro/esp8266-rs41 - A tool for reconfiguring RS41s via its serial port
|
||||
|
||||
## Alternative firmware projects
|
||||
## Vaisala RS41 hardware datasheets
|
||||
|
||||
* STM32F100x8 datasheet: https://www.st.com/resource/en/datasheet/stm32f100cb.pdf
|
||||
* Si4032 datasheet: https://www.silabs.com/documents/public/data-sheets/Si4030-31-32.pdf
|
||||
* Si4030/31/32 Register Descriptions: https://www.silabs.com/documents/public/application-notes/AN466.pdf
|
||||
|
||||
## Graw DFM-17 hardware documentation
|
||||
|
||||
* https://wiki.recessim.com/view/DFM-17_Radiosonde - Reverse-engineered documentation on the DFM-17 hardware
|
||||
|
||||
## Graw DFM-17 hardware datasheets
|
||||
|
||||
* STM32F100x8 datasheet: https://www.st.com/resource/en/datasheet/stm32f100cb.pdf
|
||||
* Si4063 datasheet: https://www.silabs.com/documents/public/data-sheets/Si4063-60-C.pdf
|
||||
* Programming Guide for EZRadioPRO Si4x6x Devices: https://www.silabs.com/documents/public/application-notes/AN633.pdf
|
||||
* Si4x6x API Documentation: http://www.silabs.com/documents/public/application-notes/EZRadioPRO_REVC2_API.zip
|
||||
|
||||
## Alternative RS41 firmware projects (only for RS41!)
|
||||
|
||||
* https://github.com/df8oe/RS41HUP - The original amateur radio firmware for RS41
|
||||
* https://github.com/darksidelemm/RS41HUP - A fork of the original firmware that includes support for Horus 4FSK (but omits APRS)
|
||||
|
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -27,6 +27,8 @@
|
|||
* $he - Heading in degrees (up to 3 chars)
|
||||
* $pc - Pulse counter value (wraps to zero at 65535, 16-bit unsigned value)
|
||||
* $ri - Radiation intensity in µR/h (up to 5 chars)
|
||||
* $ct - Clock calibration trim value (0-31, only for DFM-17)
|
||||
* $cc - Clock calibration change count (only for DFM-17)
|
||||
*
|
||||
* Allowed message lengths:
|
||||
*
|
||||
|
@ -56,6 +58,7 @@ volatile bool system_initialized = false;
|
|||
* Maximum length: 64 characters.
|
||||
*/
|
||||
char *cw_message_templates[] = {
|
||||
"$cs",
|
||||
// "$cs $loc6 $altm $gs km/h $tiC",
|
||||
// "$cs $loc6",
|
||||
// "$alt m",
|
||||
|
@ -83,7 +86,7 @@ char *aprs_comment_templates[] = {
|
|||
// " B$bu $loc12 $hh:$mm:$ss - " APRS_COMMENT,
|
||||
// " $loc12 - " APRS_COMMENT,
|
||||
// " $teC $hu% $prmb PC $pc RI $ri uR/h - " APRS_COMMENT,
|
||||
// " " APRS_COMMENT,
|
||||
" " APRS_COMMENT,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
108
src/config.h
108
src/config.h
|
@ -1,9 +1,21 @@
|
|||
#ifndef __CONFIG_H
|
||||
#define __CONFIG_H
|
||||
|
||||
// Define radiosonde type. Remove the "//" comment to select either RS41 or DFM17.
|
||||
//#define RS41
|
||||
//#define DFM17
|
||||
|
||||
#if !defined(RS41) && !defined(DFM17)
|
||||
#error "No hardware type specified. Please define RS41 or DFM17."
|
||||
#endif
|
||||
#if defined(RS41) && defined(DFM17)
|
||||
#error "Please define either RS41 or DFM17."
|
||||
#endif
|
||||
|
||||
|
||||
// Enable semihosting to receive debug logs during development
|
||||
// See the README for details on how to set up debugging and debug logs with GDB
|
||||
// NOTE: Semihosting has to be disabled when the RS41 radiosonde is not connected to an STM32 programmer dongle, otherwise the firmware will not run.
|
||||
// NOTE: Semihosting has to be disabled when the radiosonde is not connected to an STM32 programmer dongle, otherwise the firmware will not run.
|
||||
//#define SEMIHOSTING_ENABLE
|
||||
//#define LOGGING_ENABLE
|
||||
|
||||
|
@ -25,30 +37,6 @@
|
|||
// Allow powering off the sonde by pressing the button for over a second (when the sonde is not transmitting)
|
||||
#define ALLOW_POWER_OFF false
|
||||
|
||||
// Define the I²C bus clock speed in Hz.
|
||||
// The default of 100000 (= 100 kHz) should be used with the Si5351 clock generator to allow fast frequency changes.
|
||||
// Note that some BMP280 sensors may require decreasing the clock speed to 10000 (= 10 kHz)
|
||||
#define I2C_BUS_CLOCK_SPEED 100000
|
||||
|
||||
// Enable use of an externally connected I²C BMP280/BME280 atmospheric sensor
|
||||
// NOTE: Only BME280 sensors will report humidity. For BMP280 humidity readings will be zero.
|
||||
#define SENSOR_BMP280_ENABLE false
|
||||
// BMP280/BME280 I²C device address is usually 0x76 or 0x77.
|
||||
#define SENSOR_BMP280_I2C_ADDRESS 0x77
|
||||
|
||||
// Enable use of an externally connected I²C RadSens radiation sensor
|
||||
#define SENSOR_RADSENS_ENABLE false
|
||||
// Expected RadSens chip ID to verify initialization of the sensor, default is 0x7D.
|
||||
#define SENSOR_RADSENS_CHIP_ID 0x7D
|
||||
// RadSens I²C device address, default is 0x66.
|
||||
#define SENSOR_RADSENS_I2C_ADDRESS 0x66
|
||||
// Uncomment to set RadSens sensor sensitivity (imp/MKR). The default value is 105 imp/MKR.
|
||||
// The value is stored in the non-volatile memory of the microcontroller.
|
||||
#define SENSOR_RADSENS_SENSITIVITY 105
|
||||
|
||||
// Enable use of an externally connected I²C Si5351 clock generator chip for HF radio transmissions
|
||||
#define RADIO_SI5351_ENABLE false
|
||||
|
||||
// Number of character pairs to include in locator
|
||||
#define LOCATOR_PAIR_COUNT_FULL 6 // max. 6 (12 characters WWL)
|
||||
|
||||
|
@ -82,6 +70,34 @@
|
|||
#error GPS NMEA output via serial port cannot be enabled simultaneously with the I2C bus.
|
||||
#endif
|
||||
|
||||
/**
|
||||
* RS41 only: Global configuration (there is no I²C bus exposed in DFM-17)
|
||||
*/
|
||||
|
||||
// Define the I²C bus clock speed in Hz.
|
||||
// The default of 100000 (= 100 kHz) should be used with the Si5351 clock generator to allow fast frequency changes.
|
||||
// Note that some BMP280 sensors may require decreasing the clock speed to 10000 (= 10 kHz)
|
||||
#define I2C_BUS_CLOCK_SPEED 100000
|
||||
|
||||
// Enable use of an externally connected I²C BMP280/BME280 atmospheric sensor
|
||||
// NOTE: Only BME280 sensors will report humidity. For BMP280 humidity readings will be zero.
|
||||
#define SENSOR_BMP280_ENABLE false
|
||||
// BMP280/BME280 I²C device address is usually 0x76 or 0x77.
|
||||
#define SENSOR_BMP280_I2C_ADDRESS 0x77
|
||||
|
||||
// Enable use of an externally connected I²C RadSens radiation sensor
|
||||
#define SENSOR_RADSENS_ENABLE false
|
||||
// Expected RadSens chip ID to verify initialization of the sensor, default is 0x7D.
|
||||
#define SENSOR_RADSENS_CHIP_ID 0x7D
|
||||
// RadSens I²C device address, default is 0x66.
|
||||
#define SENSOR_RADSENS_I2C_ADDRESS 0x66
|
||||
// Uncomment to set RadSens sensor sensitivity (imp/MKR). The default value is 105 imp/MKR.
|
||||
// The value is stored in the non-volatile memory of the microcontroller.
|
||||
#define SENSOR_RADSENS_SENSITIVITY 105
|
||||
|
||||
// Enable use of an externally connected I²C Si5351 clock generator chip for HF radio transmissions
|
||||
#define RADIO_SI5351_ENABLE false
|
||||
|
||||
// Enable pulse counter via expansion header pin for use with devices like Geiger counters.
|
||||
// This disables the external I²C bus and the serial port as the expansion header pin 2 (I2C2_SDA (PB11) / UART3 RX) is used for pulse input.
|
||||
// Also changes the Horus 4FSK V2 data format and adds a custom data field for pulse count.
|
||||
|
@ -110,7 +126,7 @@
|
|||
#endif
|
||||
|
||||
/**
|
||||
* Built-in Si4032 radio chip transmission configuration
|
||||
* RS41 only: Built-in Si4032 radio chip transmission configuration
|
||||
*/
|
||||
|
||||
// Si4032 transmit power: 0..7
|
||||
|
@ -148,7 +164,42 @@
|
|||
#define RADIO_SI4032_TX_FREQUENCY_HORUS_V2 432501000
|
||||
|
||||
/**
|
||||
* External Si5351 radio chip transmission configuration
|
||||
* DFM-17 only: Built-in Si4063 radio chip transmission configuration
|
||||
*/
|
||||
|
||||
// Si4063 transmit power: 0..127
|
||||
// TODO: Document Si4063 transmit power levels
|
||||
#define RADIO_SI4063_TX_POWER 127
|
||||
|
||||
// Which modes to transmit using the built-in Si4063 transmitter chip
|
||||
// The COUNT settings define the number of times that each type of transmission is repeated
|
||||
#define RADIO_SI4063_TX_CW false
|
||||
#define RADIO_SI4063_TX_CW_COUNT 1
|
||||
#define RADIO_SI4063_TX_PIP false
|
||||
#define RADIO_SI4063_TX_PIP_COUNT 6
|
||||
#define RADIO_SI4063_TX_APRS true
|
||||
#define RADIO_SI4063_TX_APRS_COUNT 2
|
||||
#define RADIO_SI4063_TX_HORUS_V1 false
|
||||
#define RADIO_SI4063_TX_HORUS_V1_COUNT 1
|
||||
#define RADIO_SI4063_TX_HORUS_V2 true
|
||||
#define RADIO_SI4063_TX_HORUS_V2_COUNT 6
|
||||
|
||||
// Continuous transmit mode can be enabled for *either* Horus V1 or V2, but not both. This disables all other transmission modes.
|
||||
// The continuous mode transmits Horus 4FSK preamble between transmissions
|
||||
// to allow Horus receivers to keep frequency synchronization at all times, which improves reception.
|
||||
#define RADIO_SI4063_TX_HORUS_V1_CONTINUOUS false
|
||||
#define RADIO_SI4063_TX_HORUS_V2_CONTINUOUS false
|
||||
|
||||
// Transmit frequencies for the Si4063 transmitter modes
|
||||
#define RADIO_SI4063_TX_FREQUENCY_CW 432500000
|
||||
#define RADIO_SI4063_TX_FREQUENCY_PIP 432500000
|
||||
#define RADIO_SI4063_TX_FREQUENCY_APRS_1200 432500000
|
||||
// Use a frequency offset to place FSK tones slightly above the defined frequency for SSB reception
|
||||
#define RADIO_SI4063_TX_FREQUENCY_HORUS_V1 432501000
|
||||
#define RADIO_SI4063_TX_FREQUENCY_HORUS_V2 432501000
|
||||
|
||||
/**
|
||||
* RS41 only: External Si5351 radio chip transmission configuration
|
||||
*/
|
||||
|
||||
// Si5351 transmit power: 0..3
|
||||
|
@ -236,6 +287,7 @@
|
|||
*/
|
||||
|
||||
#define HORUS_FREQUENCY_OFFSET_SI4032 0
|
||||
#define HORUS_FREQUENCY_OFFSET_SI4063 0
|
||||
|
||||
/**
|
||||
* Horus V1 4FSK mode settings (deprecated, please use Horus V2 mode)
|
||||
|
@ -246,6 +298,7 @@
|
|||
// Please request a new payload ID in GitHub according to the instructions at: https://github.com/projecthorus/horusdemodlib/wiki#how-do-i-transmit-it
|
||||
#define HORUS_V1_PAYLOAD_ID 0
|
||||
#define HORUS_V1_BAUD_RATE_SI4032 100
|
||||
#define HORUS_V1_BAUD_RATE_SI4063 100
|
||||
#define HORUS_V1_BAUD_RATE_SI5351 50
|
||||
#define HORUS_V1_PREAMBLE_LENGTH 16
|
||||
#define HORUS_V1_IDLE_PREAMBLE_LENGTH 32
|
||||
|
@ -265,6 +318,7 @@
|
|||
// Please request a new payload ID in GitHub according to the instructions at: https://github.com/projecthorus/horusdemodlib/wiki#how-do-i-transmit-it
|
||||
#define HORUS_V2_PAYLOAD_ID 256
|
||||
#define HORUS_V2_BAUD_RATE_SI4032 100
|
||||
#define HORUS_V2_BAUD_RATE_SI4063 100
|
||||
#define HORUS_V2_BAUD_RATE_SI5351 50
|
||||
#define HORUS_V2_PREAMBLE_LENGTH 16
|
||||
#define HORUS_V2_IDLE_PREAMBLE_LENGTH 32
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
// PARIS: 50 dot durations, 20 WPM -> 60ms per unit
|
||||
#define MORSE_WPM_TO_SYMBOL_RATE(wpm) (1000 / (60 * 20 / wpm))
|
||||
|
||||
// Experimental fast frequency change routine for Si5351, not tested
|
||||
#define SI5351_FAST_ENABLE false
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
extern bool leds_enabled;
|
||||
|
|
|
@ -0,0 +1,697 @@
|
|||
/**
|
||||
* The DFM17 radiosonde-compatible Si4063 driver code has been inspired by:
|
||||
* - pAVAR9: https://github.com/Upuaut/pAVAR9/
|
||||
* - uTrak: https://github.com/thasti/utrak/
|
||||
* - DFM17 APRS/RTTY firmware by Derek Rowland
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stm32f10x_gpio.h>
|
||||
#include <stm32f10x_tim.h>
|
||||
|
||||
#include "hal/hal.h"
|
||||
#include "hal/delay.h"
|
||||
#include "hal/spi.h"
|
||||
#include "si4063.h"
|
||||
#include "gpio.h"
|
||||
#include "log.h"
|
||||
|
||||
#define SI4063_CLOCK 25600000UL
|
||||
|
||||
#define GPIO_SI4063_SDN GPIOC
|
||||
#define GPIO_PIN_SI4063_SDN GPIO_Pin_3
|
||||
|
||||
#define GPIO_SI4063_NSEL GPIOB
|
||||
#define GPIO_PIN_SI4063_NSEL GPIO_Pin_2
|
||||
|
||||
#define GPIO_SI4063_SDI GPIOA
|
||||
#define GPIO_PIN_SI4063_SDI GPIO_Pin_7
|
||||
|
||||
#define GPIO_SI4063_GPIO2 GPIOD
|
||||
#define GPIO_PIN_SI4063_GPIO2 GPIO_Pin_0
|
||||
|
||||
#define GPIO_SI4063_GPIO3 GPIOA
|
||||
#define GPIO_PIN_SI4063_GPIO3 GPIO_Pin_4
|
||||
|
||||
#define SI4063_COMMAND_PART_INFO 0x01
|
||||
#define SI4063_COMMAND_POWER_UP 0x02
|
||||
#define SI4063_COMMAND_SET_PROPERTY 0x11
|
||||
#define SI4063_COMMAND_GPIO_PIN_CFG 0x13
|
||||
#define SI4063_COMMAND_START_TX 0x31
|
||||
#define SI4063_COMMAND_CHANGE_STATE 0x34
|
||||
#define SI4063_COMMAND_GET_ADC_READING 0x14
|
||||
#define SI4063_COMMAND_READ_CMD_BUFF 0x44
|
||||
|
||||
#define SI4063_STATE_SLEEP 0x01
|
||||
#define SI4063_STATE_SPI_ACTIVE 0x02
|
||||
#define SI4063_STATE_READY 0x03
|
||||
#define SI4063_STATE_TX_TUNE 0x05
|
||||
#define SI4063_STATE_TX 0x07
|
||||
|
||||
/**
|
||||
* Modulation settings from Derek Rowland's firmware
|
||||
*/
|
||||
|
||||
#define SI4063_DATA_RATE_APRS 4400
|
||||
|
||||
/**
|
||||
* Filters from uTrak: https://github.com/thasti/utrak
|
||||
*
|
||||
* These filters consist of low-pass filters for harmonics of the square wave modulation waveform
|
||||
* and a high-pass filter for APRS pre-emphasis.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 6dB@1200 Hz, 2400 Hz
|
||||
*/
|
||||
uint8_t si4063_filter_6db_1200_2400[9] = {0x1d, 0xe5, 0xb8, 0xaa, 0xc0, 0xf5, 0x36, 0x6b, 0x7f};
|
||||
/**
|
||||
* 3db@1200 Hz, 2400 Hz
|
||||
*/
|
||||
uint8_t si4063_filter_3db_1200_2400[9] = {0x07, 0xde, 0xbf, 0xb9, 0xd4, 0x05, 0x40, 0x6d, 0x7f};
|
||||
/**
|
||||
* LP only, 2400 Hz
|
||||
*/
|
||||
uint8_t si4063_filter_lp_2400[9] = {0xfa, 0xe5, 0xd8, 0xde, 0xf8, 0x21, 0x4f, 0x71, 0x7f};
|
||||
/**
|
||||
* LP only, 4800 Hz
|
||||
*/
|
||||
uint8_t si4063_filter_lp_4800[9] = {0xd9, 0xf1, 0x0c, 0x29, 0x44, 0x5d, 0x70, 0x7c, 0x7f};
|
||||
/**
|
||||
* LP only, 4400 Hz
|
||||
*/
|
||||
uint8_t si4063_filter_lp_4400[9] = {0xd5, 0xe9, 0x03, 0x20, 0x3d, 0x58, 0x6d, 0x7a, 0x7f};
|
||||
/**
|
||||
* 6dB@1200Hz, 4400 Hz (bad stopband)
|
||||
*/
|
||||
uint8_t si4063_filter_6db_1200_4400[9] = {0x81, 0x9f, 0xc4, 0xee, 0x18, 0x3e, 0x5c, 0x70, 0x76};
|
||||
|
||||
uint32_t current_frequency_hz = 434000000UL;
|
||||
uint32_t current_deviation_hz = 0;
|
||||
|
||||
static inline void si4063_set_chip_select(bool select)
|
||||
{
|
||||
spi_set_chip_select(GPIO_SI4063_NSEL, GPIO_PIN_SI4063_NSEL, select);
|
||||
|
||||
// Output enable time, 20ns
|
||||
for (uint32_t i = 0; i < 0xFFFF; i++);
|
||||
}
|
||||
|
||||
static int si4063_wait_for_cts()
|
||||
{
|
||||
uint16_t timeout = 0xFFFF;
|
||||
uint8_t response;
|
||||
|
||||
// Poll CTS over SPI
|
||||
do
|
||||
{
|
||||
si4063_set_chip_select(true);
|
||||
spi_send(SI4063_COMMAND_READ_CMD_BUFF);
|
||||
response = spi_read();
|
||||
si4063_set_chip_select(false);
|
||||
} while (response != 0xFF && timeout--);
|
||||
|
||||
if (timeout == 0) {
|
||||
log_error("ERROR: Si4063 timeout\n");
|
||||
}
|
||||
|
||||
return timeout > 0 ? HAL_OK : HAL_ERROR;
|
||||
}
|
||||
|
||||
static int si4063_read_response(uint8_t length, uint8_t *data)
|
||||
{
|
||||
uint16_t timeout = 0xFFFF;
|
||||
uint8_t response;
|
||||
|
||||
// Poll CTS over SPI
|
||||
do {
|
||||
si4063_set_chip_select(true);
|
||||
spi_send(SI4063_COMMAND_READ_CMD_BUFF);
|
||||
response = spi_read();
|
||||
if (response == 0xFF) {
|
||||
break;
|
||||
}
|
||||
si4063_set_chip_select(false);
|
||||
|
||||
delay_us(10);
|
||||
} while(timeout--);
|
||||
|
||||
if (timeout == 0) {
|
||||
log_error("ERROR: Si4063 timeout\n");
|
||||
si4063_set_chip_select(false);
|
||||
return HAL_ERROR;
|
||||
}
|
||||
|
||||
// Read the requested data
|
||||
while (length--) {
|
||||
*(data++) = spi_read();
|
||||
}
|
||||
|
||||
si4063_set_chip_select(false);
|
||||
|
||||
return HAL_OK;
|
||||
}
|
||||
|
||||
static void si4063_send_command(uint8_t command, uint8_t length, uint8_t *data)
|
||||
{
|
||||
si4063_wait_for_cts();
|
||||
|
||||
si4063_set_chip_select(true);
|
||||
|
||||
spi_send(command);
|
||||
|
||||
while (length--) {
|
||||
spi_send(*(data++));
|
||||
}
|
||||
|
||||
si4063_set_chip_select(false);
|
||||
}
|
||||
|
||||
static int si4063_power_up()
|
||||
{
|
||||
si4063_wait_for_cts();
|
||||
|
||||
uint8_t data[] = {
|
||||
0x01, // 0x01 = FUNC PRO - Power the chip up into EZRadio PRO functional mode.
|
||||
0x01, // 0x01 = Reference signal is derived from an external TCXO.
|
||||
(SI4063_CLOCK >> 24) & 0xFF, // VCXO frequency
|
||||
(SI4063_CLOCK >> 16) & 0xFF,
|
||||
(SI4063_CLOCK >> 8) & 0xFF,
|
||||
SI4063_CLOCK & 0xFF
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_POWER_UP, sizeof(data), data);
|
||||
|
||||
return si4063_wait_for_cts();
|
||||
}
|
||||
|
||||
static void si4603_set_shutdown(bool active)
|
||||
{
|
||||
if (active) {
|
||||
GPIO_SetBits(GPIO_SI4063_SDN, GPIO_PIN_SI4063_SDN);
|
||||
} else {
|
||||
GPIO_ResetBits(GPIO_SI4063_SDN, GPIO_PIN_SI4063_SDN);
|
||||
}
|
||||
}
|
||||
|
||||
static void si4063_set_state(uint8_t state)
|
||||
{
|
||||
log_debug("Si4063: Set state %02x\n", state);
|
||||
si4063_send_command(SI4063_COMMAND_CHANGE_STATE, 1, &state);
|
||||
}
|
||||
|
||||
void si4063_enable_tx()
|
||||
{
|
||||
log_debug("Si4063: Enable TX\n");
|
||||
si4063_set_state(SI4063_STATE_TX);
|
||||
}
|
||||
|
||||
void si4063_inhibit_tx()
|
||||
{
|
||||
log_debug("Si4063: Inhibit TX\n");
|
||||
si4063_set_state(SI4063_STATE_READY);
|
||||
}
|
||||
|
||||
void si4063_disable_tx()
|
||||
{
|
||||
// Is this needed?
|
||||
si4063_set_state(SI4063_STATE_SLEEP);
|
||||
}
|
||||
|
||||
static int si4063_get_outdiv(const uint32_t frequency_hz)
|
||||
{
|
||||
// Select the output divider according to the recommended ranges in the Si406x datasheet
|
||||
if (frequency_hz < 177000000UL) {
|
||||
return 24;
|
||||
} else if (frequency_hz < 239000000UL) {
|
||||
return 16;
|
||||
} else if (frequency_hz < 353000000UL) {
|
||||
return 12;
|
||||
} else if (frequency_hz < 525000000UL) {
|
||||
return 8;
|
||||
} else if (frequency_hz < 705000000UL) {
|
||||
return 6;
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
static int si4063_get_band(const uint32_t frequency_hz)
|
||||
{
|
||||
if (frequency_hz < 177000000UL) {
|
||||
return 5;
|
||||
} else if (frequency_hz < 239000000UL) {
|
||||
return 4;
|
||||
} else if (frequency_hz < 353000000UL) {
|
||||
return 3;
|
||||
} else if (frequency_hz < 525000000UL) {
|
||||
return 2;
|
||||
} else if (frequency_hz < 705000000UL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void si4063_set_tx_frequency(const uint32_t frequency_hz)
|
||||
{
|
||||
uint8_t outdiv, band;
|
||||
uint32_t f_pfd, n, m;
|
||||
float ratio, rest;
|
||||
|
||||
log_debug("Si4063: Set frequency %lu\n", frequency_hz);
|
||||
|
||||
outdiv = si4063_get_outdiv(frequency_hz);
|
||||
band = si4063_get_band(frequency_hz);
|
||||
|
||||
f_pfd = 2 * SI4063_CLOCK / outdiv;
|
||||
n = frequency_hz / f_pfd - 1;
|
||||
|
||||
ratio = (float) frequency_hz / f_pfd;
|
||||
rest = ratio - n;
|
||||
|
||||
m = rest * 524288UL;
|
||||
|
||||
// Set the frequency band
|
||||
{
|
||||
uint8_t data[] = {
|
||||
0x20, // 0x20 = Group MODEM
|
||||
0x01, // Set 1 property
|
||||
0x51, // 0x51 = MODEM_CLKGEN_BAND
|
||||
0x08 + band // 0x08 = SY_SEL: High Performance mode (fixed prescaler = Div-by-2). Finer tuning.
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
// Set the PLL parameters
|
||||
{
|
||||
uint8_t data[] = {
|
||||
0x40, // 0x40 = Group FREQ_CONTROL
|
||||
0x06, // Set 6 properties
|
||||
0x00, // 0x00 = Start from FREQ_CONTROL_INTE
|
||||
n, // 0 (FREQ_CONTROL_INTE): Frac-N PLL Synthesizer integer divide number.
|
||||
(m >> 16) & 0xFF, // 1 (FREQ_CONTROL_FRAC): Frac-N PLL fraction number.
|
||||
(m >> 8) & 0xFF, // 2 (FREQ_CONTROL_FRAC): Frac-N PLL fraction number.
|
||||
m & 0xFF, // 3 (FREQ_CONTROL_FRAC): Frac-N PLL fraction number.
|
||||
0x00, // 4 (FREQ_CONTROL_CHANNEL_STEP_SIZE): EZ Frequency Programming channel step size.
|
||||
0x02 // 5 (FREQ_CONTROL_CHANNEL_STEP_SIZE): EZ Frequency Programming channel step size.
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
current_frequency_hz = frequency_hz;
|
||||
|
||||
// Deviation depends on the frequency band
|
||||
si4063_set_frequency_deviation(current_deviation_hz);
|
||||
}
|
||||
|
||||
void si4063_set_tx_power(uint8_t power)
|
||||
{
|
||||
uint8_t data[] = {
|
||||
0x22, // 0x20 = Group PA
|
||||
0x01, // Set 1 property
|
||||
0x01, // 0x01 = PA_PWR_LVL
|
||||
power & 0x7F // Power level from 00..7F
|
||||
};
|
||||
|
||||
log_debug("Si4063: Set TX power %02x\n", power);
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
void si4063_set_frequency_offset(uint16_t offset)
|
||||
{
|
||||
uint8_t data[] = {
|
||||
0x20, // 0x20 = Group MODEM
|
||||
0x02, // Set 2 properties (2 bytes)
|
||||
0x0D, // 0x0D = MODEM_FREQ_OFFSET
|
||||
offset >> 8, // Upper 8 bits of the offset
|
||||
offset & 0xFF // Lower 8 bits of the offset
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
static uint32_t si4063_calculate_deviation(uint32_t deviation_hz)
|
||||
{
|
||||
uint8_t outdiv = si4063_get_outdiv(current_frequency_hz);
|
||||
|
||||
// SY_SEL = Div-by-2
|
||||
return (uint32_t) (((double) (1 << 19) * outdiv * deviation_hz) / (2 * SI4063_CLOCK));
|
||||
}
|
||||
|
||||
void si4063_set_frequency_deviation(uint32_t deviation_hz)
|
||||
{
|
||||
uint32_t deviation = si4063_calculate_deviation(deviation_hz);
|
||||
|
||||
uint8_t data[] = {
|
||||
0x20, // 0x20 = Group MODEM
|
||||
0x03, // Set 3 properties (3 bytes)
|
||||
0x0A, // 0x0A = MODEM_FREQ_DEV
|
||||
(deviation >> 16) & 0xFF,
|
||||
(deviation >> 8) & 0xFF,
|
||||
deviation & 0xFF
|
||||
};
|
||||
|
||||
log_info("Si4063: Set frequency deviation to value %lu with %lu Hz\n", deviation, deviation_hz);
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
|
||||
current_deviation_hz = deviation_hz;
|
||||
}
|
||||
|
||||
void si4063_set_modulation_type(si4063_modulation_type type)
|
||||
{
|
||||
uint8_t data[] = {
|
||||
0x20, // 0x20 = Group MODEM
|
||||
0x01, // Set 1 property
|
||||
0x00, // 0x00 = MODEM_MOD_TYPE
|
||||
0x80 | // 0x80 = Direct async mode (MCU-controlled)
|
||||
0x60 | // 0x60 = Use GPIO3 as source for direct mode modulation
|
||||
0x08 // 0x08 = Direct modulation source (MCU-controlled)
|
||||
};
|
||||
|
||||
log_debug("Si4063: Set modulation type %d\n", type);
|
||||
|
||||
switch (type) {
|
||||
case SI4063_MODULATION_TYPE_CW:
|
||||
// Pure carrier wave modulation (for modulating via frequency offset, e.g. for RTTY)
|
||||
data[3] |= 0x00;
|
||||
break;
|
||||
case SI4063_MODULATION_TYPE_OOK:
|
||||
// Direct Async Mode with OOK modulation
|
||||
data[3] |= 0x01;
|
||||
break;
|
||||
case SI4063_MODULATION_TYPE_FSK:
|
||||
// Direct Async Mode with FSK modulation
|
||||
data[3] |= 0x02;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
int32_t si4063_read_temperature_celsius_100()
|
||||
{
|
||||
uint8_t response[6];
|
||||
int32_t temperature;
|
||||
uint8_t data[] = {
|
||||
0x10, // Measure internal temperature ADC reading only
|
||||
0x00
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_GET_ADC_READING, sizeof(data), data);
|
||||
|
||||
si4063_read_response(sizeof(response), response);
|
||||
|
||||
// Calculate the temperature in C * 10
|
||||
temperature = (response[4] << 8) | response[5];
|
||||
temperature *= 568;
|
||||
temperature /= 256;
|
||||
temperature -= 2970;
|
||||
|
||||
return temperature * 10;
|
||||
}
|
||||
|
||||
uint16_t si4063_read_part_info()
|
||||
{
|
||||
uint8_t response[8];
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_PART_INFO, 0, NULL);
|
||||
|
||||
si4063_read_response(sizeof(response), response);
|
||||
|
||||
// Return part number
|
||||
return response[1] << 8 | response[2];
|
||||
}
|
||||
|
||||
inline void si4063_set_direct_mode_pin(bool high)
|
||||
{
|
||||
if (high) {
|
||||
GPIO_SetBits(GPIO_SI4063_GPIO3, GPIO_PIN_SI4063_GPIO3);
|
||||
} else {
|
||||
GPIO_ResetBits(GPIO_SI4063_GPIO3, GPIO_PIN_SI4063_GPIO3);
|
||||
}
|
||||
}
|
||||
|
||||
void si4063_configure()
|
||||
{
|
||||
{
|
||||
uint8_t data[] = {
|
||||
0x00, // 0x00 = Group GLOBAL
|
||||
0x01, // Set 1 property
|
||||
0x00, // 0x00 = GLOBAL_XO_TUNE
|
||||
0x62 // Value determined for DFM17 radiosondes
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t data[] = {
|
||||
0x00, // 0x00 = Group GLOBAL
|
||||
0x01, // Set 1 property
|
||||
0x01, // 0x00 = GLOBAL_CLK_CFG
|
||||
0x00 // No clock output needed
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t data[] = {
|
||||
0x00, // 0x00 = Group GLOBAL
|
||||
0x01, // Set 1 property
|
||||
0x03, // 0x03 = GLOBAL_CONFIG
|
||||
0x40 | // 0x40 = Reserved, needs to be set to 1
|
||||
0x20 | // 0x20 = Fast sequencer mode
|
||||
0x00 // High-performance mode
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t data[] = {
|
||||
0x01, // 0x01 = Group INT_CTL
|
||||
0x01, // Set 1 property
|
||||
0x00, // 0x00 = INT_CTL_ENABLE
|
||||
0x00 // 0x00 = Disable all hardware interrupts
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t data[] = {
|
||||
0x02, // 0x02 = Group FRR_CTL
|
||||
0x04, // Set 4 properties
|
||||
0x00, // 0x00 = FRR_CTL_A_MODE
|
||||
0x00, // Disable all FRR values
|
||||
0x00,
|
||||
0x00,
|
||||
0x00
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
{
|
||||
// Used only in synchronous mode (for GFSK modulation/filtering)
|
||||
uint8_t data[] = {
|
||||
0x10, // 0x10 = Group PREAMBLE
|
||||
0x01, // Set 1 property
|
||||
0x00, // 0x00 = PREAMBLE_TX_LENGTH
|
||||
0x00 // 0x00 = Disable preamble
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
{
|
||||
// Used only in synchronous mode (for GFSK modulation/filtering)
|
||||
uint8_t data[] = {
|
||||
0x11, // 0x11 = Group SYNC
|
||||
0x01, // Set 1 property
|
||||
0x00, // 0x00 = SYNC_CONFIG
|
||||
0x80 // 0x80 = Sync word is not transmitted
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t data[] = {
|
||||
0x22, // 0x22 = Group PA
|
||||
0x01, // Set 1 property
|
||||
0x02, // 0x02 = PA_BIAS_CLKDUTY
|
||||
0x00 // 0x00 = Complementary drive signals, 50% duty cycle. For high-power applications.
|
||||
// Alternative: 0xC0 = Single-ended drive signal, 25% duty cycle. For low-power applications.
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
}
|
||||
|
||||
// Not used yet, for future use
|
||||
void si4063_set_filter(const uint8_t *filter)
|
||||
{
|
||||
uint8_t data[12] = {
|
||||
0x20, // 0x20 = Group MODEM
|
||||
0x09, // Set 9 properties
|
||||
0x0F // 0x0F = MODEM_TX_FILTER_COEFF_8
|
||||
};
|
||||
|
||||
for (uint8_t i = 0; i < 9; i++) {
|
||||
data[3 + i] = filter[i];
|
||||
}
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
void si4063_configure_data_rate(uint32_t data_rate)
|
||||
{
|
||||
// Used only for GFSK mode filtering
|
||||
uint8_t data[] = {
|
||||
0x20, // 0x20 = Group MODEM
|
||||
0x03, // Set 3 properties
|
||||
0x03, // 0x03 = MODEM_DATA_RATE
|
||||
(data_rate >> 16) & 0xFF,
|
||||
(data_rate >> 8) & 0xFF,
|
||||
data_rate & 0xFF
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
// Not used yet, for future use
|
||||
void si4063_configure_aprs()
|
||||
{
|
||||
// For APRS in direct mode with GFSK modulation
|
||||
si4063_configure_data_rate(SI4063_DATA_RATE_APRS);
|
||||
|
||||
// Used only for GFSK mode filtering
|
||||
uint32_t nco_mod = SI4063_CLOCK / 10;
|
||||
uint8_t data[] = {
|
||||
0x20, // 0x20 = Group MODEM
|
||||
0x04, // Set 4 properties
|
||||
0x06, // 0x06 = MODEM_TX_NCO_MODE
|
||||
0x00 | // 0x00 = TX Gaussian filter oversampling ratio is 10x
|
||||
((nco_mod >> 24) & 0x03),
|
||||
(nco_mod >> 16) & 0xFF,
|
||||
(nco_mod >> 8) & 0xFF,
|
||||
nco_mod & 0xFF
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_SET_PROPERTY, sizeof(data), data);
|
||||
}
|
||||
|
||||
// Not used yet, for future use
|
||||
void si4063_configure_rtty()
|
||||
{
|
||||
// For RTTY:
|
||||
// NOTE: 0x22 sets shift at about 450 Hz for RTTY
|
||||
si4063_set_frequency_deviation(0x22);
|
||||
}
|
||||
|
||||
void si4063_configure_gpio(uint8_t gpio0, uint8_t gpio1, uint8_t gpio2, uint8_t gpio3, uint8_t drive_strength) {
|
||||
uint8_t data[] = {
|
||||
gpio0,
|
||||
gpio1,
|
||||
gpio2,
|
||||
gpio3,
|
||||
0x00, // NIRQ = Do nothing
|
||||
11, // SDO 11 = Outputs the Serial Data Out (SDO) signal for the SPI bus
|
||||
drive_strength
|
||||
};
|
||||
|
||||
si4063_send_command(SI4063_COMMAND_GPIO_PIN_CFG, sizeof(data), data);
|
||||
}
|
||||
|
||||
int si4063_init()
|
||||
{
|
||||
GPIO_InitTypeDef gpio_init;
|
||||
|
||||
// Si4063 shutdown pin
|
||||
gpio_init.GPIO_Pin = GPIO_PIN_SI4063_SDN;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init(GPIO_SI4063_SDN, &gpio_init);
|
||||
|
||||
// Si4063 chip select pin
|
||||
gpio_init.GPIO_Pin = GPIO_PIN_SI4063_NSEL;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init(GPIO_SI4063_NSEL, &gpio_init);
|
||||
|
||||
// Si4063 GPIO3 pin for direct mode transmission
|
||||
gpio_init.GPIO_Pin = GPIO_PIN_SI4063_GPIO3;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init(GPIO_SI4063_GPIO3, &gpio_init);
|
||||
|
||||
si4063_set_direct_mode_pin(false);
|
||||
|
||||
si4603_set_shutdown(false);
|
||||
delay_us(50);
|
||||
|
||||
si4063_wait_for_cts();
|
||||
|
||||
si4603_set_shutdown(true);
|
||||
delay_us(20);
|
||||
si4603_set_shutdown(false);
|
||||
delay_us(50);
|
||||
|
||||
if (si4063_power_up() != HAL_OK) {
|
||||
log_error("ERROR: Error powering up Si4063\n");
|
||||
return HAL_ERROR;
|
||||
}
|
||||
|
||||
// Assume Si4063 part number
|
||||
uint16_t part = si4063_read_part_info();
|
||||
if (part != 0x4063) {
|
||||
log_error("ERROR: Unknown or missing Si4063 part number: 0x%04x\n", part);
|
||||
return HAL_ERROR;
|
||||
}
|
||||
|
||||
si4063_configure();
|
||||
|
||||
si4063_configure_gpio(
|
||||
0x00, // GPIO0: Do nothing
|
||||
0x00, // GPIO1: Do nothing
|
||||
0x00, // GPIO2: Do nothing
|
||||
0x04, // GPIO3: Pin is configured as a CMOS input for direct mode transmissions.
|
||||
0x00 // Drive strength: HIGH
|
||||
);
|
||||
|
||||
si4063_set_tx_power(0x00);
|
||||
|
||||
si4063_set_frequency_offset(0);
|
||||
|
||||
// Set deviation to zero for non-FSK modulations
|
||||
si4063_set_frequency_deviation(0);
|
||||
|
||||
si4063_set_modulation_type(SI4063_MODULATION_TYPE_CW);
|
||||
|
||||
si4063_set_state(SI4063_STATE_READY);
|
||||
|
||||
return HAL_OK;
|
||||
}
|
||||
|
||||
void TIM1_BRK_TIM15_IRQHandler(void)
|
||||
{
|
||||
static bool pin_state = false;
|
||||
|
||||
if (TIM_GetITStatus(TIM15, TIM_IT_Update) != RESET) {
|
||||
TIM_ClearITPendingBit(TIM15, TIM_IT_Update);
|
||||
#ifdef DFM17
|
||||
// Restrict the interrupt to DFM17 only just in case this ISR gets called on RS41
|
||||
pin_state = !pin_state;
|
||||
si4063_set_direct_mode_pin(pin_state);
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef __SI4063_H
|
||||
#define __SI4063_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum _si4063_modulation_type {
|
||||
SI4063_MODULATION_TYPE_CW = 0,
|
||||
SI4063_MODULATION_TYPE_OOK,
|
||||
SI4063_MODULATION_TYPE_FSK,
|
||||
} si4063_modulation_type;
|
||||
|
||||
void si4063_enable_tx();
|
||||
void si4063_inhibit_tx();
|
||||
void si4063_disable_tx();
|
||||
void si4063_set_tx_frequency(uint32_t frequency_hz);
|
||||
void si4063_set_tx_power(uint8_t power);
|
||||
void si4063_set_frequency_offset(uint16_t offset);
|
||||
void si4063_set_frequency_deviation(uint32_t deviation);
|
||||
void si4063_set_modulation_type(si4063_modulation_type type);
|
||||
int32_t si4063_read_temperature_celsius_100();
|
||||
void si4063_set_direct_mode_pin(bool high);
|
||||
int si4063_init();
|
||||
|
||||
#endif
|
|
@ -505,7 +505,7 @@ bool ubxg6010_enable_power_save_mode()
|
|||
ubxg6010_send_packet(&packet);
|
||||
success = ubxg6010_wait_for_ack();
|
||||
if (!success) {
|
||||
log_error("GPS: Entering power-saving mode failed\n")
|
||||
log_error("GPS: Entering power-saving mode failed\n");
|
||||
}
|
||||
|
||||
return success;
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
#ifndef __GPIO_H
|
||||
#define __GPIO_H
|
||||
|
||||
#include <system_stm32f10x.h>
|
||||
#include <stm32f10x_gpio.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
|
||||
// GPIO definitions for devices we use
|
||||
|
||||
#if defined (RS41)
|
||||
|
||||
#define BANK_SHUTDOWN GPIOA
|
||||
#define PIN_SHUTDOWN GPIO_Pin_12
|
||||
|
||||
#define BANK_VOLTAGE GPIOA
|
||||
#define PIN_VOLTAGE GPIO_Pin_5
|
||||
#define ADC_VOLTAGE ADC1
|
||||
#define CHANNEL_VOLTAGE ADC_Channel_5
|
||||
|
||||
#define BANK_BUTTON GPIOA
|
||||
#define PIN_BUTTON GPIO_Pin_6
|
||||
#define ADC_BUTTON ADC1
|
||||
#define CHANNEL_BUTTON ADC_Channel_6
|
||||
|
||||
#define BANK_RED_LED GPIOB
|
||||
#define PIN_RED_LED GPIO_Pin_8
|
||||
|
||||
#define BANK_GREEN_LED GPIOB
|
||||
#define PIN_GREEN_LED GPIO_Pin_7
|
||||
|
||||
#define BANK_MOSI GPIOB
|
||||
#define PIN_MOSI GPIO_Pin_15
|
||||
#define BANK_SCK GPIOB
|
||||
#define PIN_SCK GPIO_Pin_13
|
||||
#define BANK_MISO GPIOB
|
||||
#define PIN_MISO GPIO_Pin_14
|
||||
#define APBPERIPHERAL_SPI RCC_APB1Periph_SPI2
|
||||
#define PERIPHERAL_SPI SPI2
|
||||
#define RCC_SPIPeriphClockCmd RCC_APB1PeriphClockCmd
|
||||
|
||||
#define PIN_USART_TX GPIO_Pin_9
|
||||
#define BANK_USART_TX GPIOA
|
||||
#define PIN_USART_RX GPIO_Pin_10
|
||||
#define BANK_USART_RX GPIOA
|
||||
#define USART_IRQ USART1_IRQn
|
||||
#define USART_IT USART1
|
||||
#define APBPERIPHERAL_USART RCC_APB2Periph_USART1
|
||||
#define USART_IRQ_HANDLER USART1_IRQHandler
|
||||
|
||||
#elif defined (DFM17)
|
||||
|
||||
#define BANK_SHUTDOWN GPIOC
|
||||
#define PIN_SHUTDOWN GPIO_Pin_0
|
||||
|
||||
#define BANK_VOLTAGE GPIOA // Needs confirmation
|
||||
#define PIN_VOLTAGE GPIO_Pin_0 // Needs confirmation
|
||||
#define ADC_VOLTAGE ADC1 // Needs confirmation
|
||||
#define CHANNEL_VOLTAGE ADC_Channel_0 // Needs confirmation
|
||||
|
||||
#define BANK_BUTTON GPIOC
|
||||
#define PIN_BUTTON GPIO_Pin_8
|
||||
// No ADC available on the GPIOC, so we have to use digital reads/writes for the button
|
||||
|
||||
#define BANK_RED_LED GPIOB
|
||||
#define PIN_RED_LED GPIO_Pin_12
|
||||
|
||||
#define BANK_GREEN_LED GPIOC
|
||||
#define PIN_GREEN_LED GPIO_Pin_6
|
||||
|
||||
#define BANK_YELLOW_LED GPIOC
|
||||
#define PIN_YELLOW_LED GPIO_Pin_7
|
||||
|
||||
#define BANK_MOSI GPIOA
|
||||
#define PIN_MOSI GPIO_Pin_7
|
||||
#define BANK_SCK GPIOA
|
||||
#define PIN_SCK GPIO_Pin_5
|
||||
#define BANK_MISO GPIOA
|
||||
#define PIN_MISO GPIO_Pin_6
|
||||
#define APBPERIPHERAL_SPI RCC_APB2Periph_SPI1
|
||||
#define PERIPHERAL_SPI SPI1
|
||||
#define RCC_SPIPeriphClockCmd RCC_APB2PeriphClockCmd
|
||||
|
||||
#define PIN_USART_TX GPIO_Pin_2
|
||||
#define BANK_USART_TX GPIOA
|
||||
#define PIN_USART_RX GPIO_Pin_3
|
||||
#define BANK_USART_RX GPIOA
|
||||
#define USART_IRQ USART2_IRQn
|
||||
#define USART_IT USART2
|
||||
#define APBPERIPHERAL_USART RCC_APB1Periph_USART2
|
||||
#define USART_IRQ_HANDLER USART2_IRQHandler
|
||||
|
||||
#else
|
||||
Compiler error. You must define RS41 or DFM17.
|
||||
#endif // RS41 or DFM17
|
||||
|
||||
// Hardware Sanity Check
|
||||
|
||||
#if defined (RS41) && defined (DFM17)
|
||||
Compiler error. You must define RS41 or DFM17 but not both.
|
||||
#endif
|
||||
|
||||
#endif // __GPIO_H
|
|
@ -0,0 +1,144 @@
|
|||
#include "config.h"
|
||||
|
||||
#ifdef DFM17
|
||||
|
||||
#include "stm32f10x_exti.h"
|
||||
#include "stm32f10x_gpio.h"
|
||||
#include "stm32f10x.h"
|
||||
#include "stm32f10x_rcc.h"
|
||||
#include "misc.h"
|
||||
#include "system.h"
|
||||
#include "millis.h"
|
||||
#include "clock_calibration.h"
|
||||
|
||||
// The HSI (internal oscillator) trim register mask, copied from stm_lib/src/stm32f10x_rcc.c
|
||||
#define CR_HSITRIM_Mask ((uint32_t)0xFFFFFF07)
|
||||
|
||||
// Register definition for reading the HSI current trim out of the Calibration Register (CR).
|
||||
// Resulting value will be between 0-31.
|
||||
#define CURRENT_TRIM ((RCC->CR & ~CR_HSITRIM_Mask) >>3)
|
||||
|
||||
/**
|
||||
* On the DFM-17, GPIO PB8 is wired to the GPS Timepulse. We take advantage of this to do a
|
||||
* processor speed calibration. HSITRIM[4:0] allows for 32 values to adjust the HSI clock
|
||||
* speed. The center (16) value is "neutral". Each trim value above or below 16 adjusts
|
||||
* the clock by approximately 40kHZ (0.5% of the 8MHZ clock speed) (per AN2868).
|
||||
* 0.5% is about 5ms per second, so if we detect that we're off by more than 5 milliseconds between time pulses,
|
||||
* we will suggest a recalibration. The "trim_suggestion" variable is a static that will be maintained
|
||||
* by the time pulse IRQ and can be used at any time it's convenient to adjust the clock speed.
|
||||
*/
|
||||
|
||||
// Defaults, will be set it in the init routine below.
|
||||
int trim_suggestion = 16;
|
||||
int trim_current = 16;
|
||||
|
||||
uint32_t old_millis = 0;
|
||||
uint16_t calibration_change_count = 0;
|
||||
|
||||
bool calibration_indicator_state = true;
|
||||
|
||||
uint8_t clock_calibration_get_trim()
|
||||
{
|
||||
return CURRENT_TRIM;
|
||||
}
|
||||
|
||||
uint16_t clock_calibration_get_change_count()
|
||||
{
|
||||
return calibration_change_count;
|
||||
}
|
||||
|
||||
void clock_calibration_adjust()
|
||||
{
|
||||
if (trim_suggestion == trim_current) {
|
||||
return;
|
||||
}
|
||||
|
||||
RCC_AdjustHSICalibrationValue(trim_suggestion);
|
||||
trim_current = trim_suggestion;
|
||||
|
||||
calibration_change_count++;
|
||||
|
||||
calibration_indicator_state = !calibration_indicator_state;
|
||||
system_set_yellow_led(calibration_indicator_state);
|
||||
}
|
||||
|
||||
void timepulse_init()
|
||||
{
|
||||
// Initialize pin PB8 as floating input
|
||||
GPIO_InitTypeDef gpio_init;
|
||||
gpio_init.GPIO_Pin = GPIO_Pin_8;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_10MHz;
|
||||
GPIO_Init(GPIOB, &gpio_init);
|
||||
|
||||
// PB8 is connected to interrupt line 8, set trigger on the configured edge and enable the interrupt
|
||||
EXTI_InitTypeDef exti_init;
|
||||
exti_init.EXTI_Line = EXTI_Line8;
|
||||
exti_init.EXTI_Mode = EXTI_Mode_Interrupt;
|
||||
exti_init.EXTI_Trigger = EXTI_Trigger_Rising;
|
||||
exti_init.EXTI_LineCmd = ENABLE;
|
||||
EXTI_Init(&exti_init);
|
||||
|
||||
// Attach interrupt line to port B
|
||||
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource8);
|
||||
|
||||
// PB8 is connected to EXTI_Line8, which has EXTI9_5_IRQn vector. Use priority 0 for now.
|
||||
NVIC_InitTypeDef NVIC_InitStruct;
|
||||
NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;
|
||||
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
|
||||
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
|
||||
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
|
||||
NVIC_Init(&NVIC_InitStruct);
|
||||
|
||||
// Pull the current calibration to start
|
||||
trim_current = CURRENT_TRIM;
|
||||
trim_suggestion = trim_current;
|
||||
|
||||
// Set the yellow LED to help identify calibration changes
|
||||
system_set_yellow_led(calibration_indicator_state);
|
||||
}
|
||||
|
||||
// This handler is (at present) only being used for the GPS time pulse interrupt,
|
||||
// so we shouldn't need to do additional testing for the cause of the interrupt.
|
||||
|
||||
void EXTI9_5_IRQHandler(void)
|
||||
{
|
||||
uint32_t current_millis = millis();
|
||||
|
||||
EXTI_ClearITPendingBit(EXTI_Line8);
|
||||
|
||||
if (old_millis == 0) {
|
||||
// First timepulse. Just store millis.
|
||||
old_millis = current_millis;
|
||||
return;
|
||||
}
|
||||
|
||||
if (current_millis < old_millis) {
|
||||
// Milliseconds value wrapped to zero. Wait for the next interrupt.
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate milliseconds since last timepulse. Ideally there were 1000.
|
||||
uint32_t millis_delta = current_millis - old_millis;
|
||||
old_millis = current_millis;
|
||||
|
||||
// If too few clicks, speed up clock. If too many, slow down.
|
||||
int delta = (int) (1000 - millis_delta) / 5;
|
||||
|
||||
// Take one step at a time in case we had a bad clock tick
|
||||
if (delta > 1) {
|
||||
delta = 1;
|
||||
}
|
||||
if (delta < -1) {
|
||||
delta = -1;
|
||||
}
|
||||
|
||||
// Don't allow calibration suggestion to go out of range
|
||||
if (((delta + trim_current) >= 0) &&
|
||||
((delta + trim_current <= 31))) {
|
||||
// If the delta makes sense, apply to the suggestion. Otherwise, skip.
|
||||
trim_suggestion = trim_current + delta;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef __CLOCK_CALIBRATION_H
|
||||
#define __CLOCK_CALIBRATION_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef DFM17
|
||||
|
||||
extern void timepulse_init();
|
||||
extern uint8_t clock_calibration_get_trim();
|
||||
extern uint16_t clock_calibration_get_change_count();
|
||||
extern void clock_calibration_adjust();
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -19,6 +19,7 @@ void data_timer_init(uint32_t baud_rate)
|
|||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
|
||||
RCC_APB2PeriphResetCmd(RCC_APB1Periph_TIM2, DISABLE);
|
||||
|
||||
// The data timer assumes a 24 MHz clock source
|
||||
tim_init.TIM_Prescaler = 24 - 1; // tick every 1/1000000 s
|
||||
tim_init.TIM_CounterMode = TIM_CounterMode_Up;
|
||||
tim_init.TIM_Period = (uint16_t) ((1000000 / baud_rate) - 1);
|
||||
|
@ -33,8 +34,8 @@ void data_timer_init(uint32_t baud_rate)
|
|||
|
||||
NVIC_InitTypeDef nvic_init;
|
||||
nvic_init.NVIC_IRQChannel = TIM2_IRQn;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 0;
|
||||
nvic_init.NVIC_IRQChannelSubPriority = 1;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 2;
|
||||
nvic_init.NVIC_IRQChannelSubPriority = 2;
|
||||
nvic_init.NVIC_IRQChannelCmd = ENABLE;
|
||||
NVIC_Init(&nvic_init);
|
||||
|
||||
|
@ -47,8 +48,8 @@ void data_timer_uninit()
|
|||
|
||||
NVIC_InitTypeDef nvic_init;
|
||||
nvic_init.NVIC_IRQChannel = TIM2_IRQn;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 0;
|
||||
nvic_init.NVIC_IRQChannelSubPriority = 1;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 2;
|
||||
nvic_init.NVIC_IRQChannelSubPriority = 2;
|
||||
nvic_init.NVIC_IRQChannelCmd = DISABLE;
|
||||
NVIC_Init(&nvic_init);
|
||||
|
||||
|
@ -60,8 +61,6 @@ void TIM2_IRQHandler(void)
|
|||
{
|
||||
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
|
||||
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
|
||||
|
||||
system_handle_data_timer_tick();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ void delay_init()
|
|||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
|
||||
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM3, DISABLE);
|
||||
|
||||
// The delay timer assumes a 24 MHz clock source
|
||||
tim_init.TIM_Prescaler = 24 - 1;
|
||||
tim_init.TIM_CounterMode = TIM_CounterMode_Up;
|
||||
tim_init.TIM_Period = 0;
|
||||
|
@ -30,7 +31,7 @@ void delay_init()
|
|||
|
||||
NVIC_InitTypeDef nvic_init;
|
||||
nvic_init.NVIC_IRQChannel = TIM3_IRQn;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 0;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 1;
|
||||
nvic_init.NVIC_IRQChannelSubPriority = 1;
|
||||
nvic_init.NVIC_IRQChannelCmd = ENABLE;
|
||||
NVIC_Init(&nvic_init);
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
#include <stm32f10x_rcc.h>
|
||||
#include <stm32f10x_tim.h>
|
||||
#include <misc.h>
|
||||
|
||||
#include "src/hal/millis.h"
|
||||
|
||||
static uint32_t millis_counter;
|
||||
|
||||
void millis_timer_init(void)
|
||||
{
|
||||
TIM_DeInit(TIM7);
|
||||
|
||||
TIM_TimeBaseInitTypeDef tim_init;
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
|
||||
RCC_APB2PeriphResetCmd(RCC_APB1Periph_TIM7, DISABLE);
|
||||
|
||||
// The data timer assumes a 24 MHz clock source
|
||||
tim_init.TIM_Prescaler = 24 - 1; // tick every 1/1000000 s
|
||||
tim_init.TIM_CounterMode = TIM_CounterMode_Up;
|
||||
tim_init.TIM_Period = (uint16_t) (1000 - 1); // set up period of 1 millisecond
|
||||
tim_init.TIM_ClockDivision = TIM_CKD_DIV1;
|
||||
tim_init.TIM_RepetitionCounter = 0;
|
||||
|
||||
TIM_TimeBaseInit(TIM7, &tim_init);
|
||||
|
||||
TIM_ClearITPendingBit(TIM7, TIM_IT_Update);
|
||||
TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE);
|
||||
|
||||
NVIC_InitTypeDef nvic_init;
|
||||
nvic_init.NVIC_IRQChannel = TIM7_IRQn;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 0;
|
||||
nvic_init.NVIC_IRQChannelSubPriority = 1;
|
||||
nvic_init.NVIC_IRQChannelCmd = ENABLE;
|
||||
NVIC_Init(&nvic_init);
|
||||
|
||||
TIM_Cmd(TIM7, ENABLE);
|
||||
}
|
||||
|
||||
void millis_timer_uninit()
|
||||
{
|
||||
TIM_Cmd(TIM7, DISABLE);
|
||||
|
||||
NVIC_InitTypeDef nvic_init;
|
||||
nvic_init.NVIC_IRQChannel = TIM7_IRQn;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 0;
|
||||
nvic_init.NVIC_IRQChannelSubPriority = 1;
|
||||
nvic_init.NVIC_IRQChannelCmd = DISABLE;
|
||||
NVIC_Init(&nvic_init);
|
||||
|
||||
TIM_ITConfig(TIM7, TIM_IT_Update, DISABLE);
|
||||
TIM_ClearITPendingBit(TIM7, TIM_IT_Update);
|
||||
}
|
||||
|
||||
void TIM7_IRQHandler(void)
|
||||
{
|
||||
if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET) {
|
||||
TIM_ClearITPendingBit(TIM7, TIM_IT_Update);
|
||||
millis_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t millis(void)
|
||||
{
|
||||
return millis_counter;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef __MILLIS_H
|
||||
#define __MILLIS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern void millis_timer_init(void);
|
||||
extern void millis_timer_uninit();
|
||||
|
||||
extern uint32_t millis();
|
||||
|
||||
#endif
|
||||
|
121
src/hal/pwm.c
121
src/hal/pwm.c
|
@ -42,8 +42,8 @@ void pwm_data_timer_init()
|
|||
|
||||
NVIC_InitTypeDef nvic_init;
|
||||
nvic_init.NVIC_IRQChannel = TIM2_IRQn;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 0;
|
||||
nvic_init.NVIC_IRQChannelSubPriority = 1;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 2;
|
||||
nvic_init.NVIC_IRQChannelSubPriority = 2;
|
||||
nvic_init.NVIC_IRQChannelCmd = ENABLE;
|
||||
NVIC_Init(&nvic_init);
|
||||
*/
|
||||
|
@ -51,12 +51,6 @@ void pwm_data_timer_init()
|
|||
TIM_Cmd(TIM2, ENABLE);
|
||||
}
|
||||
|
||||
void pwm_data_timer_dma_request_enable(bool enabled)
|
||||
{
|
||||
// TIM2 Update DMA requests are routed to DMA1 Channel2
|
||||
TIM_DMACmd(TIM2, TIM_DMA_Update, enabled ? ENABLE : DISABLE);
|
||||
}
|
||||
|
||||
void pwm_data_timer_uninit()
|
||||
{
|
||||
TIM_Cmd(TIM2, DISABLE);
|
||||
|
@ -65,7 +59,9 @@ void pwm_data_timer_uninit()
|
|||
void pwm_timer_init(uint32_t frequency_hz_100)
|
||||
{
|
||||
TIM_DeInit(TIM15);
|
||||
#ifdef RS41
|
||||
GPIO_PinRemapConfig(GPIO_Remap_TIM15, DISABLE);
|
||||
#endif
|
||||
// Not needed: AFIO->MAPR2 |= AFIO_MAPR2_TIM15_REMAP;
|
||||
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM15, ENABLE);
|
||||
|
@ -83,6 +79,7 @@ void pwm_timer_init(uint32_t frequency_hz_100)
|
|||
|
||||
TIM_TimeBaseInit(TIM15, &tim_init);
|
||||
|
||||
#ifdef RS41
|
||||
TIM_OCInitTypeDef TIM15_OCInitStruct;
|
||||
|
||||
TIM_OCStructInit(&TIM15_OCInitStruct);
|
||||
|
@ -91,6 +88,7 @@ void pwm_timer_init(uint32_t frequency_hz_100)
|
|||
TIM15_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
|
||||
TIM15_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
|
||||
|
||||
// TIM15 channel 2 can be used to drive pin PB15, which is connected to RS41 Si4032 SDI pin for direct modulation
|
||||
TIM_OC2Init(TIM15, &TIM15_OCInitStruct);
|
||||
|
||||
// These are not needed?
|
||||
|
@ -104,10 +102,68 @@ void pwm_timer_init(uint32_t frequency_hz_100)
|
|||
TIM_OC2FastConfig(TIM15, TIM_OCFast_Enable);
|
||||
|
||||
TIM_CtrlPWMOutputs(TIM15, DISABLE);
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
// For DFM17 we don't have a PWM pin in the right place, so we manually toggle the pin in the ISR
|
||||
TIM_ClearITPendingBit(TIM15, TIM_IT_Update);
|
||||
TIM_ITConfig(TIM15, TIM_IT_Update, ENABLE);
|
||||
|
||||
NVIC_InitTypeDef nvic_init;
|
||||
nvic_init.NVIC_IRQChannel = TIM1_BRK_TIM15_IRQn;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 2;
|
||||
nvic_init.NVIC_IRQChannelSubPriority = 1;
|
||||
nvic_init.NVIC_IRQChannelCmd = ENABLE;
|
||||
NVIC_Init(&nvic_init);
|
||||
#endif
|
||||
|
||||
TIM_Cmd(TIM15, ENABLE);
|
||||
}
|
||||
|
||||
void pwm_timer_pwm_enable(bool enabled)
|
||||
{
|
||||
#ifdef RS41
|
||||
TIM_CtrlPWMOutputs(TIM15, enabled ? ENABLE : DISABLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void pwm_timer_use(bool use)
|
||||
{
|
||||
#ifdef RS41
|
||||
// Remapping the TIM15 outputs will allow TIM15 channel 2 can be used to drive pin PB15,
|
||||
// which is connected to RS41 Si4032 SDI pin for direct modulation
|
||||
GPIO_PinRemapConfig(GPIO_Remap_TIM15, use ? ENABLE : DISABLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void pwm_timer_uninit()
|
||||
{
|
||||
TIM_CtrlPWMOutputs(TIM15, DISABLE);
|
||||
TIM_Cmd(TIM15, DISABLE);
|
||||
|
||||
TIM_DeInit(TIM15);
|
||||
|
||||
#ifdef RS41
|
||||
GPIO_PinRemapConfig(GPIO_Remap_TIM15, DISABLE);
|
||||
#endif
|
||||
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM15, DISABLE);
|
||||
}
|
||||
|
||||
inline uint16_t pwm_calculate_period(uint32_t frequency_hz_100)
|
||||
{
|
||||
return (uint16_t) (((100.0f * 1000000.0f) / (frequency_hz_100 * 2.0f))) - 1;
|
||||
}
|
||||
|
||||
inline void pwm_timer_set_frequency(uint32_t pwm_period)
|
||||
{
|
||||
TIM_SetAutoreload(TIM15, pwm_period);
|
||||
}
|
||||
|
||||
/**
|
||||
* Below are experimental DMA routines for supplying PWM data for APRS modulation.
|
||||
* This does not work correctly, but is left for future reference.
|
||||
*/
|
||||
|
||||
static void pwm_dma_init_channel()
|
||||
{
|
||||
DMA_InitTypeDef dma_init;
|
||||
|
@ -146,8 +202,8 @@ void pwm_dma_init()
|
|||
|
||||
NVIC_InitTypeDef nvic_init;
|
||||
nvic_init.NVIC_IRQChannel = DMA1_Channel2_IRQn;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 0;
|
||||
nvic_init.NVIC_IRQChannelSubPriority = 1;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 2;
|
||||
nvic_init.NVIC_IRQChannelSubPriority = 0;
|
||||
nvic_init.NVIC_IRQChannelCmd = ENABLE;
|
||||
NVIC_Init(&nvic_init);
|
||||
}
|
||||
|
@ -172,6 +228,12 @@ void pwm_dma_stop()
|
|||
//pwm_dma_interrupt_enable(false);
|
||||
}
|
||||
|
||||
void pwm_data_timer_dma_request_enable(bool enabled)
|
||||
{
|
||||
// TIM2 Update DMA requests are routed to DMA1 Channel2
|
||||
TIM_DMACmd(TIM2, TIM_DMA_Update, enabled ? ENABLE : DISABLE);
|
||||
}
|
||||
|
||||
void DMA1_Channel2_IRQHandler(void)
|
||||
{
|
||||
if (DMA_GetITStatus(DMA1_IT_TE2)) {
|
||||
|
@ -187,42 +249,3 @@ void DMA1_Channel2_IRQHandler(void)
|
|||
pwm_handle_dma_transfer_full(PWM_TIMER_DMA_BUFFER_SIZE, pwm_timer_dma_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void pwm_timer_pwm_enable(bool enabled)
|
||||
{
|
||||
TIM_CtrlPWMOutputs(TIM15, enabled ? ENABLE : DISABLE);
|
||||
}
|
||||
|
||||
void pwm_timer_use(bool use)
|
||||
{
|
||||
GPIO_PinRemapConfig(GPIO_Remap_TIM15, use ? ENABLE : DISABLE);
|
||||
}
|
||||
|
||||
void pwm_timer_uninit()
|
||||
{
|
||||
TIM_CtrlPWMOutputs(TIM15, DISABLE);
|
||||
TIM_Cmd(TIM15, DISABLE);
|
||||
|
||||
TIM_DeInit(TIM15);
|
||||
|
||||
GPIO_PinRemapConfig(GPIO_Remap_TIM15, DISABLE);
|
||||
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM15, DISABLE);
|
||||
}
|
||||
|
||||
inline uint16_t pwm_calculate_period(uint32_t frequency_hz_100)
|
||||
{
|
||||
return (uint16_t) (((100.0f * 1000000.0f) / (frequency_hz_100 * 2.0f))) - 1;
|
||||
}
|
||||
|
||||
inline void pwm_timer_set_frequency(uint32_t pwm_period)
|
||||
{
|
||||
// TIM_CtrlPWMOutputs(TIM15, DISABLE);
|
||||
// TIM_Cmd(TIM15, DISABLE);
|
||||
|
||||
TIM_SetAutoreload(TIM15, pwm_period);
|
||||
// TIM_SetCompare2(TIM15, pwm_period / 2);
|
||||
|
||||
// TIM_Cmd(TIM15, ENABLE);
|
||||
// TIM_CtrlPWMOutputs(TIM15, ENABLE);
|
||||
}
|
||||
|
|
127
src/hal/spi.c
127
src/hal/spi.c
|
@ -3,85 +3,152 @@
|
|||
#include <stm32f10x_spi.h>
|
||||
|
||||
#include "spi.h"
|
||||
#include "gpio.h"
|
||||
|
||||
void spi_init()
|
||||
{
|
||||
GPIO_InitTypeDef gpio_init;
|
||||
|
||||
// SPI2_SCK & SPI2_MOSI
|
||||
gpio_init.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
|
||||
// SCK
|
||||
gpio_init.GPIO_Pin = PIN_SCK;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_AF_PP;
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init(GPIOB, &gpio_init);
|
||||
GPIO_Init(BANK_SCK, &gpio_init);
|
||||
|
||||
// SPI2_MISO
|
||||
gpio_init.GPIO_Pin = GPIO_Pin_14;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||||
// MOSI
|
||||
gpio_init.GPIO_Pin = PIN_MOSI;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_AF_PP;
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init(GPIOB, &gpio_init);
|
||||
GPIO_Init(BANK_MOSI, &gpio_init);
|
||||
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
|
||||
// MISO
|
||||
gpio_init.GPIO_Pin = PIN_MISO;
|
||||
#ifdef RS41
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_IPU;
|
||||
#endif
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init(BANK_MISO, &gpio_init);
|
||||
|
||||
RCC_SPIPeriphClockCmd(APBPERIPHERAL_SPI, ENABLE);
|
||||
|
||||
SPI_InitTypeDef spi_init;
|
||||
SPI_StructInit(&spi_init);
|
||||
|
||||
spi_init.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
|
||||
spi_init.SPI_Mode = SPI_Mode_Master;
|
||||
#ifdef RS41
|
||||
spi_init.SPI_DataSize = SPI_DataSize_16b;
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
spi_init.SPI_DataSize = SPI_DataSize_8b;
|
||||
#endif
|
||||
spi_init.SPI_CPOL = SPI_CPOL_Low;
|
||||
spi_init.SPI_CPHA = SPI_CPHA_1Edge;
|
||||
#ifdef RS41
|
||||
spi_init.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
spi_init.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
|
||||
#endif
|
||||
spi_init.SPI_FirstBit = SPI_FirstBit_MSB;
|
||||
#ifdef RS41
|
||||
spi_init.SPI_CRCPolynomial = 7;
|
||||
SPI_Init(SPI2, &spi_init);
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
spi_init.SPI_CRCPolynomial = 10;
|
||||
spi_init.SPI_NSS = SPI_NSS_Soft;
|
||||
#endif
|
||||
SPI_Init(PERIPHERAL_SPI, &spi_init);
|
||||
|
||||
SPI_SSOutputCmd(SPI2, ENABLE);
|
||||
#ifdef RS41
|
||||
SPI_SSOutputCmd(PERIPHERAL_SPI, ENABLE);
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
SPI_CalculateCRC(PERIPHERAL_SPI, DISABLE);
|
||||
#endif
|
||||
|
||||
SPI_Cmd(SPI2, ENABLE);
|
||||
SPI_Init(SPI2, &spi_init);
|
||||
SPI_Cmd(PERIPHERAL_SPI, ENABLE);
|
||||
#ifdef RS41
|
||||
// TODO: Why is this call even here?
|
||||
SPI_Init(PERIPHERAL_SPI, &spi_init);
|
||||
#endif
|
||||
}
|
||||
|
||||
void spi_uninit()
|
||||
{
|
||||
SPI_I2S_DeInit(SPI2);
|
||||
SPI_Cmd(SPI2, DISABLE);
|
||||
SPI_SSOutputCmd(SPI2, DISABLE);
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, DISABLE);
|
||||
SPI_I2S_DeInit(PERIPHERAL_SPI);
|
||||
SPI_Cmd(PERIPHERAL_SPI, DISABLE);
|
||||
SPI_SSOutputCmd(PERIPHERAL_SPI, DISABLE);
|
||||
RCC_SPIPeriphClockCmd(APBPERIPHERAL_SPI, DISABLE);
|
||||
|
||||
GPIO_InitTypeDef gpio_init;
|
||||
|
||||
gpio_init.GPIO_Pin = GPIO_Pin_14;
|
||||
gpio_init.GPIO_Pin = PIN_MISO;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_AF_PP;
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init(GPIOB, &gpio_init);
|
||||
GPIO_Init(BANK_MISO, &gpio_init);
|
||||
|
||||
gpio_init.GPIO_Pin = GPIO_Pin_15;
|
||||
gpio_init.GPIO_Pin = PIN_MOSI;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_AF_PP; // was: GPIO_Mode_Out_PP; // GPIO_Mode_AF_PP
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init(GPIOB, &gpio_init);
|
||||
GPIO_Init(BANK_MOSI, &gpio_init);
|
||||
}
|
||||
|
||||
inline void spi_send(uint16_t data)
|
||||
void spi_send(uint16_t data)
|
||||
{
|
||||
// Wait for TX buffer
|
||||
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
|
||||
SPI_I2S_SendData(SPI2, data);
|
||||
while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_TXE) == RESET);
|
||||
SPI_I2S_SendData(PERIPHERAL_SPI, data);
|
||||
#ifdef DFM17
|
||||
while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_TXE) == RESET);
|
||||
while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_BSY) == SET);
|
||||
|
||||
// Reset the overrun error by reading the data and status registers
|
||||
// NOTE: It seems this sequence is required to make Si4063 SPI communication work on DFM17 radiosondes
|
||||
SPI_I2S_ReceiveData(PERIPHERAL_SPI);
|
||||
SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_OVR);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint8_t spi_receive()
|
||||
uint8_t spi_receive()
|
||||
{
|
||||
// Wait for data in RX buffer
|
||||
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
|
||||
return (uint8_t) SPI_I2S_ReceiveData(SPI2);
|
||||
#ifdef DFM17
|
||||
while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_BSY) == SET);
|
||||
#endif
|
||||
while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_RXNE) == RESET);
|
||||
return (uint8_t) SPI_I2S_ReceiveData(PERIPHERAL_SPI);
|
||||
}
|
||||
|
||||
uint8_t spi_read()
|
||||
{
|
||||
while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_BSY) == SET);
|
||||
// Send dummy data to read in bidirectional mode
|
||||
SPI_I2S_SendData(PERIPHERAL_SPI, 0xFF);
|
||||
// Wait for data in RX buffer
|
||||
while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_RXNE) == RESET);
|
||||
return (uint8_t) SPI_I2S_ReceiveData(PERIPHERAL_SPI);
|
||||
}
|
||||
|
||||
void spi_set_chip_select(GPIO_TypeDef *gpio_cs, uint16_t pin_cs, bool select)
|
||||
{
|
||||
if (select) {
|
||||
GPIO_ResetBits(gpio_cs, pin_cs);
|
||||
} else {
|
||||
GPIO_SetBits(gpio_cs, pin_cs);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t spi_send_and_receive(GPIO_TypeDef *gpio_cs, uint16_t pin_cs, uint16_t data) {
|
||||
GPIO_ResetBits(gpio_cs, pin_cs);
|
||||
|
||||
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
|
||||
SPI_I2S_SendData(SPI2, data);
|
||||
while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_TXE) == RESET);
|
||||
SPI_I2S_SendData(PERIPHERAL_SPI, data);
|
||||
|
||||
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
|
||||
while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_RXNE) == RESET);
|
||||
GPIO_SetBits(gpio_cs, pin_cs);
|
||||
return (uint8_t) SPI_I2S_ReceiveData(SPI2);
|
||||
return (uint8_t) SPI_I2S_ReceiveData(PERIPHERAL_SPI);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef __SPI_H
|
||||
#define __SPI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stm32f10x.h>
|
||||
|
||||
|
@ -12,6 +13,10 @@ void spi_send(uint16_t data);
|
|||
|
||||
uint8_t spi_receive();
|
||||
|
||||
uint8_t spi_read();
|
||||
|
||||
void spi_set_chip_select(GPIO_TypeDef *gpio_cs, uint16_t pin_cs, bool select);
|
||||
|
||||
uint8_t spi_send_and_receive(GPIO_TypeDef *gpio_cs, uint16_t pin_cs, uint16_t data);
|
||||
|
||||
#endif
|
||||
|
|
140
src/hal/system.c
140
src/hal/system.c
|
@ -11,6 +11,8 @@
|
|||
#include "system.h"
|
||||
#include "delay.h"
|
||||
#include "log.h"
|
||||
#include "gpio.h"
|
||||
#include "millis.h"
|
||||
|
||||
#define BUTTON_PRESS_LONG_COUNT SYSTEM_SCHEDULER_TIMER_TICKS_PER_SECOND
|
||||
|
||||
|
@ -38,6 +40,8 @@ static void nvic_init()
|
|||
static void rcc_init()
|
||||
{
|
||||
RCC_DeInit();
|
||||
#ifdef RS41
|
||||
// The RS41 hardware uses an external clock at 24 MHz
|
||||
RCC_HSEConfig(RCC_HSE_ON);
|
||||
|
||||
ErrorStatus hse_status = RCC_WaitForHSEStartUp();
|
||||
|
@ -45,6 +49,17 @@ static void rcc_init()
|
|||
// If HSE fails to start up, the application will have incorrect clock configuration.
|
||||
while (true) {}
|
||||
}
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
// The DFM17 hardware uses the internal clock
|
||||
RCC_AdjustHSICalibrationValue(0x10U);
|
||||
// Set up a 24 MHz PLL for 24 MHz SYSCLK (8 MHz / 2) * 6 = 24 MHz
|
||||
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_6);
|
||||
RCC_HSICmd(ENABLE);
|
||||
RCC_PLLCmd(ENABLE);
|
||||
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
|
||||
#endif
|
||||
|
||||
//SystemInit();
|
||||
|
||||
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
|
||||
|
@ -52,12 +67,21 @@ static void rcc_init()
|
|||
|
||||
// TODO: Check what the delay timer TIM3 settings really should be and WTF the clock tick really is!?!?!?
|
||||
|
||||
RCC_HCLKConfig(RCC_SYSCLK_Div1); // Was: RCC_SYSCLK_Div4
|
||||
RCC_PCLK2Config(RCC_HCLK_Div1); // Was: 4
|
||||
RCC_PCLK1Config(RCC_HCLK_Div1); // Was: 2
|
||||
RCC_HCLKConfig(RCC_SYSCLK_Div1);
|
||||
RCC_PCLK2Config(RCC_HCLK_Div1);
|
||||
RCC_PCLK1Config(RCC_HCLK_Div1);
|
||||
#ifdef RS41
|
||||
// Use the 24 MHz external clock as SYSCLK
|
||||
RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);
|
||||
|
||||
while (RCC_GetSYSCLKSource() != 0x04);
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
// Use the 24 MHz PLL as SYSCLK
|
||||
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
|
||||
|
||||
while (RCC_GetSYSCLKSource() != 0x08);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void gpio_init()
|
||||
|
@ -72,28 +96,45 @@ static void gpio_init()
|
|||
GPIO_InitTypeDef gpio_init;
|
||||
|
||||
// Shutdown request
|
||||
gpio_init.GPIO_Pin = GPIO_Pin_12;
|
||||
gpio_init.GPIO_Pin = PIN_SHUTDOWN;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init(GPIOA, &gpio_init);
|
||||
GPIO_Init(BANK_SHUTDOWN, &gpio_init);
|
||||
#ifdef DFM17
|
||||
GPIO_SetBits(BANK_SHUTDOWN, PIN_SHUTDOWN); // Pull high to keep BMS from removing battery power after startup
|
||||
#endif
|
||||
|
||||
// Battery voltage (analog)
|
||||
gpio_init.GPIO_Pin = GPIO_Pin_5;
|
||||
gpio_init.GPIO_Pin = PIN_VOLTAGE;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_AIN;
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_10MHz;
|
||||
GPIO_Init(GPIOA, &gpio_init);
|
||||
GPIO_Init(BANK_VOLTAGE, &gpio_init);
|
||||
|
||||
// Button state (analog)
|
||||
gpio_init.GPIO_Pin = GPIO_Pin_6;
|
||||
gpio_init.GPIO_Pin = PIN_BUTTON;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_AIN;
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_10MHz;
|
||||
GPIO_Init(GPIOA, &gpio_init);
|
||||
GPIO_Init(BANK_BUTTON, &gpio_init);
|
||||
|
||||
// LEDs
|
||||
gpio_init.GPIO_Pin = GPIO_PIN_LED_GREEN | GPIO_PIN_LED_RED;
|
||||
// Green LED
|
||||
gpio_init.GPIO_Pin = PIN_GREEN_LED;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init(GPIOB, &gpio_init);
|
||||
GPIO_Init(BANK_GREEN_LED, &gpio_init);
|
||||
|
||||
// Red LED
|
||||
gpio_init.GPIO_Pin = PIN_RED_LED;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init(BANK_RED_LED, &gpio_init);
|
||||
|
||||
#ifdef DFM17
|
||||
// Yellow LED (only in DFM-17)
|
||||
gpio_init.GPIO_Pin = PIN_YELLOW_LED;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init(BANK_YELLOW_LED, &gpio_init);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,7 +149,12 @@ static void dma_adc_init()
|
|||
|
||||
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
|
||||
|
||||
#ifdef RS41
|
||||
dma_init.DMA_BufferSize = 2;
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
dma_init.DMA_BufferSize = 1;
|
||||
#endif
|
||||
dma_init.DMA_DIR = DMA_DIR_PeripheralSRC;
|
||||
dma_init.DMA_M2M = DMA_M2M_Disable;
|
||||
dma_init.DMA_MemoryBaseAddr = (uint32_t) &dma_buffer_adc;
|
||||
|
@ -132,11 +178,21 @@ static void dma_adc_init()
|
|||
adc_init.ADC_ContinuousConvMode = ENABLE;
|
||||
adc_init.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
|
||||
adc_init.ADC_DataAlign = ADC_DataAlign_Right;
|
||||
#ifdef RS41
|
||||
adc_init.ADC_NbrOfChannel = 2;
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
adc_init.ADC_NbrOfChannel = 1;
|
||||
#endif
|
||||
ADC_Init(ADC1, &adc_init);
|
||||
|
||||
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_28Cycles5);
|
||||
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 2, ADC_SampleTime_28Cycles5);
|
||||
ADC_RegularChannelConfig(ADC1, CHANNEL_VOLTAGE, 1, ADC_SampleTime_28Cycles5);
|
||||
#ifdef RS41
|
||||
ADC_RegularChannelConfig(ADC1, CHANNEL_BUTTON, 2, ADC_SampleTime_28Cycles5);
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
// Not using ADC for button on DFM17
|
||||
#endif
|
||||
|
||||
// ADC1 DMA requests are routed to DMA1 Channel1
|
||||
ADC_DMACmd(ADC1, ENABLE);
|
||||
|
@ -160,12 +216,23 @@ uint16_t system_get_battery_voltage_millivolts()
|
|||
|
||||
uint16_t system_get_button_adc_value()
|
||||
{
|
||||
#ifdef RS41
|
||||
return (uint16_t) dma_buffer_adc[1];
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
// Fake being an ADC. Take the binary value and if non-zero, make it trigger button-down
|
||||
return ( ((int) GPIO_ReadInputDataBit(BANK_BUTTON,PIN_BUTTON)) * 2100);
|
||||
#endif
|
||||
}
|
||||
|
||||
void system_shutdown()
|
||||
{
|
||||
GPIO_SetBits(GPIOA, GPIO_Pin_12);
|
||||
#ifdef RS41
|
||||
GPIO_SetBits(BANK_SHUTDOWN, PIN_SHUTDOWN);
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
GPIO_ResetBits(BANK_SHUTDOWN, PIN_SHUTDOWN);
|
||||
#endif
|
||||
}
|
||||
|
||||
void system_handle_button()
|
||||
|
@ -251,20 +318,50 @@ void system_enable_tick()
|
|||
|
||||
void system_set_green_led(bool enabled)
|
||||
{
|
||||
#ifdef RS41
|
||||
if (enabled) {
|
||||
GPIO_ResetBits(GPIOB, GPIO_PIN_LED_GREEN);
|
||||
GPIO_ResetBits(BANK_GREEN_LED, PIN_GREEN_LED);
|
||||
} else {
|
||||
GPIO_SetBits(GPIOB, GPIO_PIN_LED_GREEN);
|
||||
GPIO_SetBits(BANK_GREEN_LED, PIN_GREEN_LED);
|
||||
}
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
if (enabled) {
|
||||
GPIO_SetBits(BANK_GREEN_LED, PIN_GREEN_LED);
|
||||
} else {
|
||||
GPIO_ResetBits(BANK_GREEN_LED, PIN_GREEN_LED);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void system_set_red_led(bool enabled)
|
||||
{
|
||||
#ifdef RS41
|
||||
if (enabled) {
|
||||
GPIO_ResetBits(GPIOB, GPIO_PIN_LED_RED);
|
||||
GPIO_ResetBits(BANK_RED_LED, PIN_RED_LED);
|
||||
} else {
|
||||
GPIO_SetBits(GPIOB, GPIO_PIN_LED_RED);
|
||||
GPIO_SetBits(BANK_RED_LED, PIN_RED_LED);
|
||||
}
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
if (enabled) {
|
||||
GPIO_SetBits(BANK_RED_LED, PIN_RED_LED);
|
||||
} else {
|
||||
GPIO_ResetBits(BANK_RED_LED, PIN_RED_LED);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void system_set_yellow_led(bool enabled)
|
||||
{
|
||||
#ifdef DFM17
|
||||
// Only DFM-17 has a yellow LED
|
||||
if (enabled) {
|
||||
GPIO_SetBits(BANK_YELLOW_LED, PIN_YELLOW_LED);
|
||||
} else {
|
||||
GPIO_ResetBits(BANK_YELLOW_LED, PIN_YELLOW_LED);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void system_disable_irq()
|
||||
|
@ -284,7 +381,10 @@ void system_init()
|
|||
gpio_init();
|
||||
dma_adc_init();
|
||||
delay_init();
|
||||
|
||||
#ifdef DFM17
|
||||
// The millis timer is used for clock calibration on DFM-17 only
|
||||
millis_timer_init();
|
||||
#endif
|
||||
system_scheduler_timer_init();
|
||||
|
||||
RCC_ClocksTypeDef RCC_Clocks;
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
#ifndef __HAL_SYSTEM_H
|
||||
#define __HAL_SYSTEM_H
|
||||
|
||||
#include "config.h"
|
||||
#include "hal.h"
|
||||
|
||||
#define GPIO_PIN_LED_GREEN GPIO_Pin_7
|
||||
#define GPIO_PIN_LED_RED GPIO_Pin_8
|
||||
|
||||
#define SYSTEM_SCHEDULER_TIMER_TICKS_PER_SECOND 10000
|
||||
|
||||
void system_init();
|
||||
|
@ -17,6 +15,9 @@ void system_disable_irq();
|
|||
void system_enable_irq();
|
||||
void system_set_green_led(bool enabled);
|
||||
void system_set_red_led(bool enabled);
|
||||
#ifdef DFM17
|
||||
void system_set_yellow_led(bool enabled);
|
||||
#endif
|
||||
uint16_t system_get_battery_voltage_millivolts();
|
||||
uint16_t system_get_button_adc_value();
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <misc.h>
|
||||
|
||||
#include "usart_gps.h"
|
||||
#include "gpio.h"
|
||||
|
||||
void (*usart_gps_handle_incoming_byte)(uint8_t data) = NULL;
|
||||
|
||||
|
@ -13,25 +14,29 @@ void usart_gps_init(uint32_t baud_rate, bool enable_irq)
|
|||
{
|
||||
GPIO_InitTypeDef gpio_init;
|
||||
|
||||
// USART1 TX
|
||||
gpio_init.GPIO_Pin = GPIO_Pin_9;
|
||||
// USART TX
|
||||
gpio_init.GPIO_Pin = PIN_USART_TX;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_AF_PP;
|
||||
gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init(GPIOA, &gpio_init);
|
||||
GPIO_Init(BANK_USART_TX, &gpio_init);
|
||||
|
||||
// USART1 RX
|
||||
gpio_init.GPIO_Pin = GPIO_Pin_10;
|
||||
// USART RX
|
||||
gpio_init.GPIO_Pin = PIN_USART_RX;
|
||||
gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||||
GPIO_Init(GPIOA, &gpio_init);
|
||||
GPIO_Init(BANK_USART_RX, &gpio_init);
|
||||
|
||||
NVIC_DisableIRQ(USART1_IRQn);
|
||||
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
|
||||
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
|
||||
USART_ClearITPendingBit(USART1, USART_IT_ORE);
|
||||
NVIC_DisableIRQ(USART_IRQ);
|
||||
USART_ITConfig(USART_IT, USART_IT_RXNE, DISABLE);
|
||||
USART_ClearITPendingBit(USART_IT, USART_IT_RXNE);
|
||||
USART_ClearITPendingBit(USART_IT, USART_IT_ORE);
|
||||
|
||||
USART_Cmd(USART1, DISABLE);
|
||||
USART_Cmd(USART_IT, DISABLE);
|
||||
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
|
||||
#if defined(RS41)
|
||||
RCC_APB2PeriphClockCmd(APBPERIPHERAL_USART, ENABLE);
|
||||
#elif defined(DFM17)
|
||||
RCC_APB1PeriphClockCmd(APBPERIPHERAL_USART, ENABLE);
|
||||
#endif
|
||||
|
||||
USART_InitTypeDef usart_init;
|
||||
usart_init.USART_BaudRate = baud_rate;
|
||||
|
@ -40,65 +45,69 @@ void usart_gps_init(uint32_t baud_rate, bool enable_irq)
|
|||
usart_init.USART_Parity = USART_Parity_No;
|
||||
usart_init.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
|
||||
usart_init.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
|
||||
USART_Init(USART1, &usart_init);
|
||||
USART_Init(USART_IT, &usart_init);
|
||||
|
||||
NVIC_InitTypeDef nvic_init;
|
||||
nvic_init.NVIC_IRQChannel = USART1_IRQn;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 15;
|
||||
nvic_init.NVIC_IRQChannel = USART_IRQ;
|
||||
nvic_init.NVIC_IRQChannelPreemptionPriority = 3;
|
||||
nvic_init.NVIC_IRQChannelSubPriority = 2;
|
||||
nvic_init.NVIC_IRQChannelCmd = ENABLE;
|
||||
NVIC_Init(&nvic_init);
|
||||
|
||||
USART_Cmd(USART1, ENABLE);
|
||||
USART_Cmd(USART_IT, ENABLE);
|
||||
if (enable_irq) {
|
||||
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
|
||||
NVIC_EnableIRQ(USART1_IRQn);
|
||||
USART_ITConfig(USART_IT, USART_IT_RXNE, ENABLE);
|
||||
NVIC_EnableIRQ(USART_IRQ);
|
||||
}
|
||||
}
|
||||
|
||||
static void usart_gps_enable_irq(bool enabled) {
|
||||
if (enabled) {
|
||||
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
|
||||
NVIC_EnableIRQ(USART1_IRQn);
|
||||
USART_ITConfig(USART_IT, USART_IT_RXNE, ENABLE);
|
||||
NVIC_EnableIRQ(USART_IRQ);
|
||||
} else {
|
||||
NVIC_DisableIRQ(USART1_IRQn);
|
||||
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
|
||||
NVIC_DisableIRQ(USART_IRQ);
|
||||
USART_ITConfig(USART_IT, USART_IT_RXNE, DISABLE);
|
||||
}
|
||||
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
|
||||
USART_ClearITPendingBit(USART1, USART_IT_ORE);
|
||||
USART_ClearITPendingBit(USART_IT, USART_IT_RXNE);
|
||||
USART_ClearITPendingBit(USART_IT, USART_IT_ORE);
|
||||
}
|
||||
|
||||
void usart_gps_uninit()
|
||||
{
|
||||
usart_gps_enable_irq(false);
|
||||
USART_Cmd(USART1, DISABLE);
|
||||
USART_DeInit(USART1);
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, DISABLE);
|
||||
USART_Cmd(USART_IT, DISABLE);
|
||||
USART_DeInit(USART_IT);
|
||||
#if defined(RS41)
|
||||
RCC_APB2PeriphClockCmd(APBPERIPHERAL_USART, DISABLE);
|
||||
#elif defined(DFM17)
|
||||
RCC_APB1PeriphClockCmd(APBPERIPHERAL_USART, DISABLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void usart_gps_enable(bool enabled)
|
||||
{
|
||||
usart_gps_enable_irq(enabled);
|
||||
USART_Cmd(USART1, enabled ? ENABLE : DISABLE);
|
||||
USART_Cmd(USART_IT, enabled ? ENABLE : DISABLE);
|
||||
}
|
||||
|
||||
void usart_gps_send_byte(uint8_t data)
|
||||
{
|
||||
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}
|
||||
USART_SendData(USART1, data);
|
||||
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}
|
||||
while (USART_GetFlagStatus(USART_IT, USART_FLAG_TC) == RESET) {}
|
||||
USART_SendData(USART_IT, data);
|
||||
while (USART_GetFlagStatus(USART_IT, USART_FLAG_TC) == RESET) {}
|
||||
}
|
||||
|
||||
void USART1_IRQHandler(void)
|
||||
void USART_IRQ_HANDLER(void)
|
||||
{
|
||||
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
|
||||
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
|
||||
uint8_t data = (uint8_t) USART_ReceiveData(USART1);
|
||||
if (USART_GetITStatus(USART_IT, USART_IT_RXNE) != RESET) {
|
||||
USART_ClearITPendingBit(USART_IT, USART_IT_RXNE);
|
||||
uint8_t data = (uint8_t) USART_ReceiveData(USART_IT);
|
||||
usart_gps_handle_incoming_byte(data);
|
||||
} else if (USART_GetITStatus(USART1, USART_IT_ORE) != RESET) {
|
||||
USART_ClearITPendingBit(USART1, USART_IT_ORE);
|
||||
USART_ReceiveData(USART1);
|
||||
} else if (USART_GetITStatus(USART_IT, USART_IT_ORE) != RESET) {
|
||||
USART_ClearITPendingBit(USART_IT, USART_IT_ORE);
|
||||
USART_ReceiveData(USART_IT);
|
||||
} else {
|
||||
USART_ReceiveData(USART1);
|
||||
USART_ReceiveData(USART_IT);
|
||||
}
|
||||
}
|
||||
|
|
39
src/main.c
39
src/main.c
|
@ -1,12 +1,10 @@
|
|||
#include "hal/system.h"
|
||||
#include "hal/i2c.h"
|
||||
#include "hal/spi.h"
|
||||
#include "hal/usart_gps.h"
|
||||
#include "hal/usart_ext.h"
|
||||
#include "hal/delay.h"
|
||||
#include "hal/datatimer.h"
|
||||
#include "drivers/ubxg6010/ubxg6010.h"
|
||||
#include "drivers/si4032/si4032.h"
|
||||
#include "drivers/pulse_counter/pulse_counter.h"
|
||||
#include "bmp280_handler.h"
|
||||
#include "radsens_handler.h"
|
||||
|
@ -15,6 +13,16 @@
|
|||
#include "config.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifdef RS41
|
||||
#include "hal/i2c.h"
|
||||
#include "drivers/si4032/si4032.h"
|
||||
#endif
|
||||
|
||||
#ifdef DFM17
|
||||
#include "hal/clock_calibration.h"
|
||||
#include "drivers/si4063/si4063.h"
|
||||
#endif
|
||||
|
||||
uint32_t counter = 0;
|
||||
bool led_state = true;
|
||||
|
||||
|
@ -67,6 +75,17 @@ void set_red_led(bool enabled)
|
|||
system_set_red_led(enabled);
|
||||
}
|
||||
|
||||
#ifdef DFM17
|
||||
void set_yellow_led(bool enabled)
|
||||
{
|
||||
if ((LEDS_DISABLE_ALTITUDE_METERS > 0) && (current_gps_data.altitude_mm / 1000 > LEDS_DISABLE_ALTITUDE_METERS)) {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
system_set_yellow_led(enabled);
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(void)
|
||||
{
|
||||
bool success;
|
||||
|
@ -89,8 +108,11 @@ int main(void)
|
|||
log_info("Pulse counter init\n");
|
||||
pulse_counter_init(PULSE_COUNTER_PIN_MODE, PULSE_COUNTER_INTERRUPT_EDGE);
|
||||
} else {
|
||||
#ifdef RS41
|
||||
// Only RS41 uses the I2C bus
|
||||
log_info("I2C init: clock speed %d kHz\n", I2C_BUS_CLOCK_SPEED / 1000);
|
||||
i2c_init(I2C_BUS_CLOCK_SPEED);
|
||||
#endif
|
||||
}
|
||||
|
||||
log_info("SPI init\n");
|
||||
|
@ -105,8 +127,18 @@ int main(void)
|
|||
goto gps_init;
|
||||
}
|
||||
|
||||
#ifdef DFM17
|
||||
log_info("Timepulse init\n");
|
||||
timepulse_init();
|
||||
#endif
|
||||
|
||||
#if defined(RS41)
|
||||
log_info("Si4032 init\n");
|
||||
si4032_init();
|
||||
#elif defined(DFM17)
|
||||
log_info("Si4063 init\n");
|
||||
si4063_init();
|
||||
#endif
|
||||
|
||||
if (bmp280_enabled) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
|
@ -160,6 +192,9 @@ int main(void)
|
|||
|
||||
while (true) {
|
||||
radio_handle_main_loop();
|
||||
#ifdef DFM17
|
||||
clock_calibration_adjust();
|
||||
#endif
|
||||
//NVIC_SystemLPConfig(NVIC_LP_SEVONPEND, DISABLE);
|
||||
//__WFI();
|
||||
}
|
||||
|
|
186
src/radio.c
186
src/radio.c
|
@ -13,7 +13,12 @@
|
|||
#include "codecs/jtencode/jtencode.h"
|
||||
#include "drivers/ubxg6010/ubxg6010.h"
|
||||
#include "radio_internal.h"
|
||||
#ifdef RS41
|
||||
#include "radio_si4032.h"
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
#include "radio_si4063.h"
|
||||
#endif
|
||||
#include "radio_si5351.h"
|
||||
#include "radio_payload_cw.h"
|
||||
#include "radio_payload_aprs_position.h"
|
||||
|
@ -25,6 +30,8 @@
|
|||
#include "radio_payload_fsq.h"
|
||||
|
||||
radio_transmit_entry radio_transmit_schedule[] = {
|
||||
#ifdef RS41
|
||||
// Si4032
|
||||
#if RADIO_SI4032_TX_HORUS_V1_CONTINUOUS
|
||||
{
|
||||
.enabled = RADIO_SI4032_TX_HORUS_V1,
|
||||
|
@ -155,6 +162,145 @@ radio_transmit_entry radio_transmit_schedule[] = {
|
|||
.fsk_encoder_api = &mfsk_fsk_encoder_api,
|
||||
},
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DFM17
|
||||
// Si4063
|
||||
#if RADIO_SI4063_TX_HORUS_V1_CONTINUOUS
|
||||
{
|
||||
.enabled = RADIO_SI4063_TX_HORUS_V1,
|
||||
.radio_type = RADIO_TYPE_SI4063,
|
||||
.data_mode = RADIO_DATA_MODE_HORUS_V1,
|
||||
.time_sync_seconds = HORUS_V1_TIME_SYNC_SECONDS,
|
||||
.time_sync_seconds_offset = HORUS_V1_TIME_SYNC_OFFSET_SECONDS,
|
||||
.frequency = RADIO_SI4063_TX_FREQUENCY_HORUS_V1,
|
||||
.tx_power = RADIO_SI4063_TX_POWER,
|
||||
.symbol_rate = HORUS_V1_BAUD_RATE_SI4063,
|
||||
.payload_encoder = &radio_horus_v1_payload_encoder,
|
||||
.fsk_encoder_api = &mfsk_fsk_encoder_api,
|
||||
},
|
||||
{
|
||||
.enabled = RADIO_SI4063_TX_HORUS_V1,
|
||||
.radio_type = RADIO_TYPE_SI4063,
|
||||
.data_mode = RADIO_DATA_MODE_HORUS_V1,
|
||||
.time_sync_seconds = HORUS_V1_TIME_SYNC_SECONDS,
|
||||
.time_sync_seconds_offset = HORUS_V1_TIME_SYNC_OFFSET_SECONDS,
|
||||
.frequency = RADIO_SI4063_TX_FREQUENCY_HORUS_V1,
|
||||
.tx_power = RADIO_SI4063_TX_POWER,
|
||||
.symbol_rate = HORUS_V1_BAUD_RATE_SI4063,
|
||||
.payload_encoder = &radio_horus_v1_idle_encoder,
|
||||
.fsk_encoder_api = &mfsk_fsk_encoder_api,
|
||||
},
|
||||
#elif RADIO_SI4063_TX_HORUS_V2_CONTINUOUS
|
||||
{
|
||||
.enabled = RADIO_SI4063_TX_HORUS_V2,
|
||||
.radio_type = RADIO_TYPE_SI4063,
|
||||
.data_mode = RADIO_DATA_MODE_HORUS_V2,
|
||||
.time_sync_seconds = HORUS_V2_TIME_SYNC_SECONDS,
|
||||
.time_sync_seconds_offset = HORUS_V2_TIME_SYNC_OFFSET_SECONDS,
|
||||
.frequency = RADIO_SI4063_TX_FREQUENCY_HORUS_V2,
|
||||
.tx_power = RADIO_SI4063_TX_POWER,
|
||||
.symbol_rate = HORUS_V2_BAUD_RATE_SI4063,
|
||||
.payload_encoder = &radio_horus_v2_payload_encoder,
|
||||
.fsk_encoder_api = &mfsk_fsk_encoder_api,
|
||||
},
|
||||
{
|
||||
.enabled = RADIO_SI4063_TX_HORUS_V2,
|
||||
.radio_type = RADIO_TYPE_SI4063,
|
||||
.data_mode = RADIO_DATA_MODE_HORUS_V2,
|
||||
.time_sync_seconds = HORUS_V2_TIME_SYNC_SECONDS,
|
||||
.time_sync_seconds_offset = HORUS_V2_TIME_SYNC_OFFSET_SECONDS,
|
||||
.frequency = RADIO_SI4063_TX_FREQUENCY_HORUS_V2,
|
||||
.tx_power = RADIO_SI4063_TX_POWER,
|
||||
.symbol_rate = HORUS_V2_BAUD_RATE_SI4063,
|
||||
.payload_encoder = &radio_horus_v2_idle_encoder,
|
||||
.fsk_encoder_api = &mfsk_fsk_encoder_api,
|
||||
},
|
||||
#else
|
||||
#if RADIO_SI4063_TX_PIP
|
||||
{
|
||||
.enabled = RADIO_SI4063_TX_PIP,
|
||||
.radio_type = RADIO_TYPE_SI4063,
|
||||
.data_mode = RADIO_DATA_MODE_PIP,
|
||||
.transmit_count = RADIO_SI4063_TX_PIP_COUNT,
|
||||
.time_sync_seconds = PIP_TIME_SYNC_SECONDS,
|
||||
.time_sync_seconds_offset = PIP_TIME_SYNC_OFFSET_SECONDS,
|
||||
.frequency = RADIO_SI4063_TX_FREQUENCY_PIP,
|
||||
.tx_power = RADIO_SI4063_TX_POWER,
|
||||
.symbol_rate = MORSE_WPM_TO_SYMBOL_RATE(PIP_SPEED_WPM),
|
||||
.payload_encoder = &radio_cw_payload_encoder,
|
||||
.fsk_encoder_api = &morse_fsk_encoder_api,
|
||||
},
|
||||
#endif
|
||||
#if RADIO_SI4063_TX_CW
|
||||
{
|
||||
.enabled = RADIO_SI4063_TX_CW,
|
||||
.radio_type = RADIO_TYPE_SI4063,
|
||||
.data_mode = RADIO_DATA_MODE_CW,
|
||||
.transmit_count = RADIO_SI4063_TX_CW_COUNT,
|
||||
.time_sync_seconds = CW_TIME_SYNC_SECONDS,
|
||||
.time_sync_seconds_offset = CW_TIME_SYNC_OFFSET_SECONDS,
|
||||
.frequency = RADIO_SI4063_TX_FREQUENCY_CW,
|
||||
.tx_power = RADIO_SI4063_TX_POWER,
|
||||
.symbol_rate = MORSE_WPM_TO_SYMBOL_RATE(CW_SPEED_WPM),
|
||||
.payload_encoder = &radio_cw_payload_encoder,
|
||||
.fsk_encoder_api = &morse_fsk_encoder_api,
|
||||
},
|
||||
#endif
|
||||
#if RADIO_SI4063_TX_APRS
|
||||
{
|
||||
.enabled = RADIO_SI4063_TX_APRS,
|
||||
.radio_type = RADIO_TYPE_SI4063,
|
||||
.data_mode = RADIO_DATA_MODE_APRS_1200,
|
||||
.transmit_count = RADIO_SI4063_TX_APRS_COUNT,
|
||||
.time_sync_seconds = APRS_TIME_SYNC_SECONDS,
|
||||
.time_sync_seconds_offset = APRS_TIME_SYNC_OFFSET_SECONDS,
|
||||
.frequency = RADIO_SI4063_TX_FREQUENCY_APRS_1200,
|
||||
.tx_power = RADIO_SI4063_TX_POWER,
|
||||
.symbol_rate = 1200,
|
||||
#if APRS_WEATHER_REPORT_ENABLE
|
||||
.payload_encoder = &radio_aprs_weather_report_payload_encoder,
|
||||
#else
|
||||
.payload_encoder = &radio_aprs_position_payload_encoder,
|
||||
#endif
|
||||
.fsk_encoder_api = &bell_fsk_encoder_api,
|
||||
},
|
||||
#endif
|
||||
#if RADIO_SI4063_TX_HORUS_V1
|
||||
{
|
||||
.enabled = RADIO_SI4063_TX_HORUS_V1,
|
||||
.radio_type = RADIO_TYPE_SI4063,
|
||||
.data_mode = RADIO_DATA_MODE_HORUS_V1,
|
||||
.transmit_count = RADIO_SI4063_TX_HORUS_V1_COUNT,
|
||||
.time_sync_seconds = HORUS_V1_TIME_SYNC_SECONDS,
|
||||
.time_sync_seconds_offset = HORUS_V1_TIME_SYNC_OFFSET_SECONDS,
|
||||
.frequency = RADIO_SI4063_TX_FREQUENCY_HORUS_V1,
|
||||
.tx_power = RADIO_SI4063_TX_POWER,
|
||||
.symbol_rate = HORUS_V1_BAUD_RATE_SI4063,
|
||||
.payload_encoder = &radio_horus_v1_payload_encoder,
|
||||
.fsk_encoder_api = &mfsk_fsk_encoder_api,
|
||||
},
|
||||
#endif
|
||||
#if RADIO_SI4063_TX_HORUS_V2
|
||||
{
|
||||
.enabled = RADIO_SI4063_TX_HORUS_V2,
|
||||
.radio_type = RADIO_TYPE_SI4063,
|
||||
.data_mode = RADIO_DATA_MODE_HORUS_V2,
|
||||
.transmit_count = RADIO_SI4063_TX_HORUS_V2_COUNT,
|
||||
.time_sync_seconds = HORUS_V2_TIME_SYNC_SECONDS,
|
||||
.time_sync_seconds_offset = HORUS_V2_TIME_SYNC_OFFSET_SECONDS,
|
||||
.frequency = RADIO_SI4063_TX_FREQUENCY_HORUS_V2,
|
||||
.tx_power = RADIO_SI4063_TX_POWER,
|
||||
.symbol_rate = HORUS_V2_BAUD_RATE_SI4063,
|
||||
.payload_encoder = &radio_horus_v2_payload_encoder,
|
||||
.fsk_encoder_api = &mfsk_fsk_encoder_api,
|
||||
},
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Si5351
|
||||
#if RADIO_SI5351_ENABLE
|
||||
#if RADIO_SI5351_TX_PIP
|
||||
{
|
||||
|
@ -300,7 +446,6 @@ radio_transmit_entry radio_transmit_schedule[] = {
|
|||
.fsk_encoder_api = &jtencode_fsk_encoder_api,
|
||||
},
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
{
|
||||
.end = true,
|
||||
|
@ -329,6 +474,8 @@ uint16_t radio_current_payload_length = 0;
|
|||
|
||||
uint8_t radio_current_symbol_data[RADIO_SYMBOL_DATA_MAX_LENGTH];
|
||||
|
||||
uint32_t precalculated_pwm_periods[FSK_TONE_COUNT_MAX];
|
||||
|
||||
static volatile uint32_t start_tick = 0, end_tick = 0;
|
||||
|
||||
telemetry_data current_telemetry_data;
|
||||
|
@ -515,9 +662,16 @@ static bool radio_start_transmit(radio_transmit_entry *entry)
|
|||
}
|
||||
|
||||
switch (entry->radio_type) {
|
||||
#ifdef RS41
|
||||
case RADIO_TYPE_SI4032:
|
||||
success = radio_start_transmit_si4032(entry, &radio_shared_state);
|
||||
break;
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
case RADIO_TYPE_SI4063:
|
||||
success = radio_start_transmit_si4063(entry, &radio_shared_state);
|
||||
break;
|
||||
#endif
|
||||
case RADIO_TYPE_SI5351:
|
||||
success = radio_start_transmit_si5351(entry, &radio_shared_state);
|
||||
break;
|
||||
|
@ -562,9 +716,16 @@ static bool radio_stop_transmit(radio_transmit_entry *entry)
|
|||
bool success;
|
||||
|
||||
switch (entry->radio_type) {
|
||||
#ifdef RS41
|
||||
case RADIO_TYPE_SI4032:
|
||||
success = radio_stop_transmit_si4032(entry, &radio_shared_state);
|
||||
break;
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
case RADIO_TYPE_SI4063:
|
||||
success = radio_stop_transmit_si4063(entry, &radio_shared_state);
|
||||
break;
|
||||
#endif
|
||||
case RADIO_TYPE_SI5351:
|
||||
success = radio_stop_transmit_si5351(entry, &radio_shared_state);
|
||||
break;
|
||||
|
@ -620,9 +781,16 @@ static bool radio_transmit_symbol(radio_transmit_entry *entry)
|
|||
bool success;
|
||||
|
||||
switch (entry->radio_type) {
|
||||
#ifdef RS41
|
||||
case RADIO_TYPE_SI4032:
|
||||
success = radio_transmit_symbol_si4032(entry, &radio_shared_state);
|
||||
break;
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
case RADIO_TYPE_SI4063:
|
||||
success = radio_transmit_symbol_si4063(entry, &radio_shared_state);
|
||||
break;
|
||||
#endif
|
||||
case RADIO_TYPE_SI5351:
|
||||
success = radio_transmit_symbol_si5351(entry, &radio_shared_state);
|
||||
break;
|
||||
|
@ -687,7 +855,12 @@ void radio_handle_timer_tick()
|
|||
|
||||
void radio_handle_data_timer_tick()
|
||||
{
|
||||
#ifdef RS41
|
||||
radio_handle_data_timer_si4032();
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
radio_handle_data_timer_si4063();
|
||||
#endif
|
||||
|
||||
radio_handle_data_timer_si5351();
|
||||
}
|
||||
|
@ -788,7 +961,13 @@ void radio_handle_main_loop()
|
|||
radio_start_transmit_entry = radio_current_transmit_entry;
|
||||
}
|
||||
|
||||
#ifdef RS41
|
||||
radio_handle_main_loop_si4032(radio_current_transmit_entry, &radio_shared_state);
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
radio_handle_main_loop_si4063(radio_current_transmit_entry, &radio_shared_state);
|
||||
#endif
|
||||
|
||||
radio_handle_main_loop_si5351(radio_current_transmit_entry, &radio_shared_state);
|
||||
|
||||
bool first_symbol = false;
|
||||
|
@ -901,7 +1080,12 @@ void radio_init()
|
|||
radio_current_transmit_entry = &radio_transmit_schedule[radio_current_transmit_entry_index];
|
||||
}
|
||||
|
||||
#ifdef RS41
|
||||
radio_init_si4032();
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
radio_init_si4063();
|
||||
#endif
|
||||
|
||||
radio_module_initialized = true;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
typedef enum _radio_type {
|
||||
RADIO_TYPE_SI4032 = 1,
|
||||
RADIO_TYPE_SI5351,
|
||||
RADIO_TYPE_SI4063,
|
||||
} radio_type;
|
||||
|
||||
typedef enum _radio_data_mode {
|
||||
|
@ -80,5 +81,6 @@ typedef struct _radio_module_state {
|
|||
|
||||
extern radio_transmit_entry *radio_current_transmit_entry;
|
||||
extern radio_module_state radio_shared_state;
|
||||
extern uint32_t precalculated_pwm_periods[];
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#include <stdint.h>
|
||||
#include "config.h"
|
||||
|
||||
#ifdef RS41
|
||||
#include <string.h>
|
||||
|
||||
#include "hal/system.h"
|
||||
|
@ -27,14 +29,13 @@
|
|||
static bool si4032_use_dma = false;
|
||||
|
||||
// TODO: Add support for multiple APRS baud rates
|
||||
uint16_t symbol_delay_bell_202_1200bps_us = 823;
|
||||
// This delay is for RS41 radiosondes
|
||||
#define symbol_delay_bell_202_1200bps_us 823
|
||||
|
||||
static volatile bool radio_si4032_state_change = false;
|
||||
static volatile uint32_t radio_si4032_freq = 0;
|
||||
static volatile int8_t radio_dma_transfer_stop_after_counter = -1;
|
||||
|
||||
uint32_t precalculated_pwm_periods[FSK_TONE_COUNT_MAX];
|
||||
|
||||
uint16_t radio_si4032_fill_pwm_buffer(uint16_t offset, uint16_t length, uint16_t *buffer);
|
||||
|
||||
bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state *shared_state)
|
||||
|
@ -258,7 +259,7 @@ inline void radio_handle_data_timer_si4032()
|
|||
|
||||
tone_index = fsk_encoder_api->next_tone(fsk_enc);
|
||||
if (tone_index < 0) {
|
||||
log_info("Horus V1 TX finished\n");
|
||||
log_info("Horus TX finished\n");
|
||||
radio_shared_state.radio_interrupt_transmit_active = false;
|
||||
radio_shared_state.radio_transmission_finished = true;
|
||||
system_enable_tick();
|
||||
|
@ -405,3 +406,4 @@ void radio_init_si4032()
|
|||
pwm_dma_init();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,307 @@
|
|||
#include "config.h"
|
||||
|
||||
#ifdef DFM17
|
||||
#include "hal/system.h"
|
||||
#include "hal/spi.h"
|
||||
#include "hal/pwm.h"
|
||||
#include "hal/delay.h"
|
||||
#include "hal/datatimer.h"
|
||||
#include "drivers/si4063/si4063.h"
|
||||
#include "log.h"
|
||||
|
||||
#include "radio_si4063.h"
|
||||
#include "codecs/mfsk/mfsk.h"
|
||||
|
||||
#define SI4063_DEVIATION_HZ_RTTY 200.0
|
||||
#define SI4063_DEVIATION_HZ_APRS 2600.0
|
||||
|
||||
#define CW_SYMBOL_RATE_MULTIPLIER 4
|
||||
|
||||
// TODO: Add support for multiple APRS baud rates
|
||||
// This delay is for DFM-17 radiosondes
|
||||
#define symbol_delay_bell_202_1200bps_us 821
|
||||
|
||||
static volatile bool radio_si4063_state_change = false;
|
||||
static volatile uint32_t radio_si4063_freq = 0;
|
||||
|
||||
bool radio_start_transmit_si4063(radio_transmit_entry *entry, radio_module_state *shared_state)
|
||||
{
|
||||
uint16_t frequency_offset;
|
||||
uint32_t frequency_deviation = 0;
|
||||
si4063_modulation_type modulation_type;
|
||||
bool use_direct_mode;
|
||||
|
||||
switch (entry->data_mode) {
|
||||
case RADIO_DATA_MODE_CW:
|
||||
case RADIO_DATA_MODE_PIP:
|
||||
frequency_offset = 1;
|
||||
modulation_type = SI4063_MODULATION_TYPE_OOK;
|
||||
use_direct_mode = false;
|
||||
|
||||
data_timer_init(entry->symbol_rate * CW_SYMBOL_RATE_MULTIPLIER);
|
||||
break;
|
||||
case RADIO_DATA_MODE_RTTY:
|
||||
frequency_offset = 0;
|
||||
frequency_deviation = SI4063_DEVIATION_HZ_RTTY;
|
||||
modulation_type = SI4063_MODULATION_TYPE_CW;
|
||||
use_direct_mode = false;
|
||||
break;
|
||||
case RADIO_DATA_MODE_APRS_1200:
|
||||
frequency_offset = 0;
|
||||
frequency_deviation = SI4063_DEVIATION_HZ_APRS;
|
||||
modulation_type = SI4063_MODULATION_TYPE_FSK;
|
||||
use_direct_mode = true;
|
||||
break;
|
||||
case RADIO_DATA_MODE_HORUS_V1:
|
||||
case RADIO_DATA_MODE_HORUS_V2: {
|
||||
fsk_tone *idle_tone = mfsk_get_idle_tone(&entry->fsk_encoder);
|
||||
frequency_offset = (uint16_t) idle_tone->index + HORUS_FREQUENCY_OFFSET_SI4063;
|
||||
modulation_type = SI4063_MODULATION_TYPE_CW;
|
||||
use_direct_mode = false;
|
||||
|
||||
data_timer_init(entry->fsk_encoder_api->get_symbol_rate(&entry->fsk_encoder));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
si4063_set_tx_frequency(entry->frequency);
|
||||
si4063_set_tx_power(entry->tx_power);
|
||||
si4063_set_frequency_offset(frequency_offset);
|
||||
si4063_set_modulation_type(modulation_type);
|
||||
si4063_set_frequency_deviation(frequency_deviation);
|
||||
|
||||
si4063_enable_tx();
|
||||
|
||||
if (use_direct_mode) {
|
||||
spi_uninit();
|
||||
pwm_timer_init(100 * 100); // TODO: Idle tone
|
||||
pwm_timer_use(true);
|
||||
pwm_timer_pwm_enable(true);
|
||||
}
|
||||
|
||||
switch (entry->data_mode) {
|
||||
case RADIO_DATA_MODE_CW:
|
||||
case RADIO_DATA_MODE_PIP:
|
||||
spi_uninit();
|
||||
system_disable_tick();
|
||||
shared_state->radio_interrupt_transmit_active = true;
|
||||
break;
|
||||
case RADIO_DATA_MODE_APRS_1200:
|
||||
shared_state->radio_manual_transmit_active = true;
|
||||
break;
|
||||
case RADIO_DATA_MODE_HORUS_V1:
|
||||
case RADIO_DATA_MODE_HORUS_V2:
|
||||
system_disable_tick();
|
||||
shared_state->radio_interrupt_transmit_active = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t radio_next_symbol_si4063(radio_transmit_entry *entry, radio_module_state *shared_state)
|
||||
{
|
||||
switch (entry->data_mode) {
|
||||
case RADIO_DATA_MODE_CW:
|
||||
case RADIO_DATA_MODE_PIP:
|
||||
return 0;
|
||||
case RADIO_DATA_MODE_RTTY:
|
||||
return 0;
|
||||
case RADIO_DATA_MODE_APRS_1200: {
|
||||
int8_t next_tone_index = entry->fsk_encoder_api->next_tone(&entry->fsk_encoder);
|
||||
if (next_tone_index < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shared_state->radio_current_fsk_tones[next_tone_index].frequency_hz_100;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool radio_transmit_symbol_si4063(radio_transmit_entry *entry, radio_module_state *shared_state)
|
||||
{
|
||||
uint32_t frequency = radio_next_symbol_si4063(entry, shared_state);
|
||||
|
||||
if (frequency == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
radio_si4063_freq = frequency;
|
||||
radio_si4063_state_change = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void radio_handle_main_loop_manual_si4063(radio_transmit_entry *entry, radio_module_state *shared_state)
|
||||
{
|
||||
fsk_encoder_api *fsk_encoder_api = entry->fsk_encoder_api;
|
||||
fsk_encoder *fsk_enc = &entry->fsk_encoder;
|
||||
|
||||
for (uint8_t i = 0; i < shared_state->radio_current_fsk_tone_count; i++) {
|
||||
precalculated_pwm_periods[i] = pwm_calculate_period(shared_state->radio_current_fsk_tones[i].frequency_hz_100);
|
||||
}
|
||||
|
||||
system_disable_tick();
|
||||
|
||||
switch (entry->data_mode) {
|
||||
case RADIO_DATA_MODE_APRS_1200: {
|
||||
int8_t tone_index;
|
||||
|
||||
while ((tone_index = fsk_encoder_api->next_tone(fsk_enc)) >= 0) {
|
||||
pwm_timer_set_frequency(precalculated_pwm_periods[tone_index]);
|
||||
shared_state->radio_symbol_count_loop++;
|
||||
delay_us(symbol_delay_bell_202_1200bps_us);
|
||||
}
|
||||
|
||||
radio_si4063_state_change = false;
|
||||
shared_state->radio_transmission_finished = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
system_enable_tick();
|
||||
}
|
||||
|
||||
void radio_handle_main_loop_si4063(radio_transmit_entry *entry, radio_module_state *shared_state)
|
||||
{
|
||||
if (entry->radio_type != RADIO_TYPE_SI4063 || shared_state->radio_interrupt_transmit_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shared_state->radio_manual_transmit_active) {
|
||||
radio_handle_main_loop_manual_si4063(entry, shared_state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (radio_si4063_state_change) {
|
||||
radio_si4063_state_change = false;
|
||||
pwm_timer_set_frequency(radio_si4063_freq);
|
||||
shared_state->radio_symbol_count_loop++;
|
||||
}
|
||||
}
|
||||
|
||||
inline void radio_handle_data_timer_si4063()
|
||||
{
|
||||
static int cw_symbol_rate_multiplier = CW_SYMBOL_RATE_MULTIPLIER;
|
||||
|
||||
if (radio_current_transmit_entry->radio_type != RADIO_TYPE_SI4063 || !radio_shared_state.radio_interrupt_transmit_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (radio_current_transmit_entry->data_mode) {
|
||||
case RADIO_DATA_MODE_CW:
|
||||
case RADIO_DATA_MODE_PIP: {
|
||||
cw_symbol_rate_multiplier--;
|
||||
if (cw_symbol_rate_multiplier > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
cw_symbol_rate_multiplier = CW_SYMBOL_RATE_MULTIPLIER;
|
||||
|
||||
fsk_encoder_api *fsk_encoder_api = radio_current_transmit_entry->fsk_encoder_api;
|
||||
fsk_encoder *fsk_enc = &radio_current_transmit_entry->fsk_encoder;
|
||||
int8_t tone_index;
|
||||
|
||||
tone_index = fsk_encoder_api->next_tone(fsk_enc);
|
||||
if (tone_index < 0) {
|
||||
si4063_set_direct_mode_pin(false);
|
||||
log_info("CW TX finished\n");
|
||||
radio_shared_state.radio_interrupt_transmit_active = false;
|
||||
radio_shared_state.radio_transmission_finished = true;
|
||||
system_enable_tick();
|
||||
break;
|
||||
}
|
||||
|
||||
si4063_set_direct_mode_pin(tone_index == 0 ? false : true);
|
||||
|
||||
radio_shared_state.radio_symbol_count_interrupt++;
|
||||
break;
|
||||
}
|
||||
case RADIO_DATA_MODE_HORUS_V1:
|
||||
case RADIO_DATA_MODE_HORUS_V2: {
|
||||
fsk_encoder_api *fsk_encoder_api = radio_current_transmit_entry->fsk_encoder_api;
|
||||
fsk_encoder *fsk_enc = &radio_current_transmit_entry->fsk_encoder;
|
||||
int8_t tone_index;
|
||||
|
||||
tone_index = fsk_encoder_api->next_tone(fsk_enc);
|
||||
if (tone_index < 0) {
|
||||
log_info("Horus TX finished\n");
|
||||
radio_shared_state.radio_interrupt_transmit_active = false;
|
||||
radio_shared_state.radio_transmission_finished = true;
|
||||
system_enable_tick();
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE: The factor of 23 will produce a tone spacing of about 270 Hz, which is the standard spacing for Horus 4FSK
|
||||
si4063_set_frequency_offset(tone_index * 23 + HORUS_FREQUENCY_OFFSET_SI4063);
|
||||
|
||||
radio_shared_state.radio_symbol_count_interrupt++;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool radio_stop_transmit_si4063(radio_transmit_entry *entry, radio_module_state *shared_state)
|
||||
{
|
||||
bool use_direct_mode = false;
|
||||
|
||||
switch (entry->data_mode) {
|
||||
case RADIO_DATA_MODE_CW:
|
||||
case RADIO_DATA_MODE_PIP:
|
||||
data_timer_uninit();
|
||||
spi_init();
|
||||
break;
|
||||
case RADIO_DATA_MODE_RTTY:
|
||||
case RADIO_DATA_MODE_HORUS_V1:
|
||||
case RADIO_DATA_MODE_HORUS_V2:
|
||||
data_timer_uninit();
|
||||
break;
|
||||
case RADIO_DATA_MODE_APRS_1200:
|
||||
use_direct_mode = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (use_direct_mode) {
|
||||
pwm_timer_pwm_enable(false);
|
||||
pwm_timer_use(false);
|
||||
pwm_timer_uninit();
|
||||
spi_init();
|
||||
}
|
||||
|
||||
si4063_inhibit_tx();
|
||||
|
||||
switch (entry->data_mode) {
|
||||
case RADIO_DATA_MODE_CW:
|
||||
case RADIO_DATA_MODE_PIP:
|
||||
system_enable_tick();
|
||||
break;
|
||||
case RADIO_DATA_MODE_APRS_1200:
|
||||
break;
|
||||
case RADIO_DATA_MODE_HORUS_V1:
|
||||
case RADIO_DATA_MODE_HORUS_V2:
|
||||
system_enable_tick();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void radio_init_si4063()
|
||||
{
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef __RADIO_SI4063_H
|
||||
#define __RADIO_SI4063_H
|
||||
#ifdef DFM17
|
||||
#include "radio_internal.h"
|
||||
|
||||
bool radio_start_transmit_si4063(radio_transmit_entry *entry, radio_module_state *shared_state);
|
||||
bool radio_transmit_symbol_si4063(radio_transmit_entry *entry, radio_module_state *shared_state);
|
||||
void radio_handle_main_loop_si4063(radio_transmit_entry *entry, radio_module_state *shared_state);
|
||||
void radio_handle_data_timer_si4063();
|
||||
bool radio_stop_transmit_si4063(radio_transmit_entry *entry, radio_module_state *shared_state);
|
||||
void radio_init_si4063();
|
||||
#endif
|
||||
#endif
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
#define CW_SYMBOL_RATE_MULTIPLIER 4
|
||||
|
||||
static bool use_fast_si5351 = false;
|
||||
|
||||
static volatile bool radio_si5351_state_change = false;
|
||||
static volatile uint64_t radio_si5351_freq = 0;
|
||||
static volatile bool radio_si5351_frequency_not_set = false;
|
||||
|
@ -27,26 +25,18 @@ bool radio_start_transmit_si5351(radio_transmit_entry *entry, radio_module_state
|
|||
set_frequency_early = false;
|
||||
break;
|
||||
case RADIO_DATA_MODE_HORUS_V1:
|
||||
case RADIO_DATA_MODE_HORUS_V2:
|
||||
data_timer_init(entry->fsk_encoder_api->get_symbol_rate(&entry->fsk_encoder));
|
||||
//use_fast_si5351 = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: handle Si5351 errors
|
||||
if (use_fast_si5351) {
|
||||
si5351_set_drive_strength_fast(SI5351_CLOCK_CLK0, entry->tx_power);
|
||||
if (set_frequency_early) {
|
||||
si5351_set_frequency_fast(SI5351_CLOCK_CLK0, ((uint64_t) entry->frequency) * 100ULL);
|
||||
si5351_output_enable_fast(SI5351_CLOCK_CLK0, true);
|
||||
}
|
||||
} else {
|
||||
si5351_set_drive_strength(SI5351_CLOCK_CLK0, entry->tx_power);
|
||||
if (set_frequency_early) {
|
||||
si5351_set_frequency(SI5351_CLOCK_CLK0, ((uint64_t) entry->frequency) * 100ULL);
|
||||
si5351_output_enable(SI5351_CLOCK_CLK0, true);
|
||||
}
|
||||
si5351_set_drive_strength(SI5351_CLOCK_CLK0, entry->tx_power);
|
||||
if (set_frequency_early) {
|
||||
si5351_set_frequency(SI5351_CLOCK_CLK0, ((uint64_t) entry->frequency) * 100ULL);
|
||||
si5351_output_enable(SI5351_CLOCK_CLK0, true);
|
||||
}
|
||||
|
||||
switch (entry->data_mode) {
|
||||
|
@ -58,6 +48,7 @@ bool radio_start_transmit_si5351(radio_transmit_entry *entry, radio_module_state
|
|||
radio_si5351_frequency_not_set = true;
|
||||
break;
|
||||
case RADIO_DATA_MODE_HORUS_V1:
|
||||
case RADIO_DATA_MODE_HORUS_V2:
|
||||
system_disable_tick();
|
||||
shared_state->radio_interrupt_transmit_active = true;
|
||||
break;
|
||||
|
@ -73,8 +64,8 @@ bool radio_transmit_symbol_si5351(radio_transmit_entry *entry, radio_module_stat
|
|||
switch (entry->data_mode) {
|
||||
case RADIO_DATA_MODE_CW:
|
||||
case RADIO_DATA_MODE_PIP:
|
||||
return false;
|
||||
case RADIO_DATA_MODE_HORUS_V1:
|
||||
case RADIO_DATA_MODE_HORUS_V2:
|
||||
return false;
|
||||
default: {
|
||||
int8_t next_tone_index = entry->fsk_encoder_api->next_tone(&entry->fsk_encoder);
|
||||
|
@ -152,14 +143,15 @@ inline void radio_handle_data_timer_si5351()
|
|||
radio_shared_state.radio_symbol_count_interrupt++;
|
||||
break;
|
||||
}
|
||||
case RADIO_DATA_MODE_HORUS_V1: {
|
||||
case RADIO_DATA_MODE_HORUS_V1:
|
||||
case RADIO_DATA_MODE_HORUS_V2: {
|
||||
fsk_encoder_api *fsk_encoder_api = radio_current_transmit_entry->fsk_encoder_api;
|
||||
fsk_encoder *fsk_enc = &radio_current_transmit_entry->fsk_encoder;
|
||||
int8_t tone_index;
|
||||
|
||||
tone_index = fsk_encoder_api->next_tone(fsk_enc);
|
||||
if (tone_index < 0) {
|
||||
log_info("Horus V1 TX finished\n");
|
||||
log_info("Horus TX finished\n");
|
||||
radio_shared_state.radio_interrupt_transmit_active = false;
|
||||
radio_shared_state.radio_transmission_finished = true;
|
||||
system_enable_tick();
|
||||
|
@ -181,30 +173,13 @@ inline void radio_handle_data_timer_si5351()
|
|||
|
||||
bool radio_stop_transmit_si5351(radio_transmit_entry *entry, radio_module_state *shared_state)
|
||||
{
|
||||
switch (entry->data_mode) {
|
||||
case RADIO_DATA_MODE_CW:
|
||||
case RADIO_DATA_MODE_PIP:
|
||||
break;
|
||||
case RADIO_DATA_MODE_HORUS_V1:
|
||||
// use_fast_si5351 = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (use_fast_si5351) {
|
||||
si5351_output_enable_fast(SI5351_CLOCK_CLK0, false);
|
||||
} else {
|
||||
si5351_output_enable(SI5351_CLOCK_CLK0, false);
|
||||
}
|
||||
si5351_output_enable(SI5351_CLOCK_CLK0, false);
|
||||
|
||||
switch (entry->data_mode) {
|
||||
case RADIO_DATA_MODE_CW:
|
||||
case RADIO_DATA_MODE_PIP:
|
||||
data_timer_uninit();
|
||||
system_enable_tick();
|
||||
break;
|
||||
case RADIO_DATA_MODE_HORUS_V1:
|
||||
case RADIO_DATA_MODE_HORUS_V2:
|
||||
data_timer_uninit();
|
||||
system_enable_tick();
|
||||
break;
|
||||
|
|
|
@ -1,10 +1,64 @@
|
|||
#include "drivers/si5351/si5351.h"
|
||||
#include "config.h"
|
||||
|
||||
#if SI5351_FAST_ENABLE
|
||||
#include "drivers/si5351fast/si5351mcu.h"
|
||||
#else
|
||||
#include "drivers/si5351/si5351.h"
|
||||
#endif
|
||||
#include "si5351_handler.h"
|
||||
|
||||
Si5351 *si5351;
|
||||
#if SI5351_FAST_ENABLE
|
||||
Si5351mcu si5351_fast;
|
||||
#else
|
||||
Si5351 *si5351;
|
||||
#endif
|
||||
|
||||
#if SI5351_FAST_ENABLE
|
||||
bool si5351_handler_init()
|
||||
{
|
||||
si5351_fast.init(&DEFAULT_I2C_PORT, SIADDR);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool si5351_set_frequency(si5351_clock_id clock, uint64_t frequency_hz_100)
|
||||
{
|
||||
si5351_fast.setFreq((uint8_t) clock, frequency_hz_100 / 100L);
|
||||
return true;
|
||||
}
|
||||
|
||||
void si5351_output_enable(si5351_clock_id clock, bool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
si5351_fast.enable((uint8_t) clock);
|
||||
} else {
|
||||
si5351_fast.disable((uint8_t) clock);
|
||||
}
|
||||
}
|
||||
|
||||
void si5351_set_drive_strength(si5351_clock_id clock, uint8_t drive)
|
||||
{
|
||||
int si5351_drive;
|
||||
|
||||
switch (drive) {
|
||||
case 0:
|
||||
si5351_drive = SIOUT_2mA;
|
||||
break;
|
||||
case 1:
|
||||
si5351_drive = SIOUT_4mA;
|
||||
break;
|
||||
case 2:
|
||||
si5351_drive = SIOUT_6mA;
|
||||
break;
|
||||
case 3:
|
||||
si5351_drive = SIOUT_8mA;
|
||||
break;
|
||||
default:
|
||||
si5351_drive = SIOUT_2mA;
|
||||
}
|
||||
|
||||
si5351_fast.setPower(si5351_drive, (uint8_t) clock);
|
||||
}
|
||||
#else
|
||||
bool si5351_handler_init()
|
||||
{
|
||||
si5351 = new Si5351(&DEFAULT_I2C_PORT);
|
||||
|
@ -14,9 +68,6 @@ bool si5351_handler_init()
|
|||
if (!si5351_found) {
|
||||
return si5351_found;
|
||||
}
|
||||
|
||||
// si5351_fast.init(&DEFAULT_I2C_PORT, SIADDR);
|
||||
|
||||
return si5351_found;
|
||||
}
|
||||
|
||||
|
@ -53,42 +104,4 @@ void si5351_set_drive_strength(si5351_clock_id clock, uint8_t drive)
|
|||
|
||||
si5351->drive_strength((enum si5351_clock) clock, si5351_drive);
|
||||
}
|
||||
|
||||
bool si5351_set_frequency_fast(si5351_clock_id clock, uint64_t frequency_hz_100)
|
||||
{
|
||||
si5351_fast.setFreq((uint8_t) clock, frequency_hz_100 / 100L);
|
||||
return true;
|
||||
}
|
||||
|
||||
void si5351_output_enable_fast(si5351_clock_id clock, bool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
si5351_fast.enable((uint8_t) clock);
|
||||
} else {
|
||||
si5351_fast.disable((uint8_t) clock);
|
||||
}
|
||||
}
|
||||
|
||||
void si5351_set_drive_strength_fast(si5351_clock_id clock, uint8_t drive)
|
||||
{
|
||||
int si5351_drive;
|
||||
|
||||
switch (drive) {
|
||||
case 0:
|
||||
si5351_drive = SIOUT_2mA;
|
||||
break;
|
||||
case 1:
|
||||
si5351_drive = SIOUT_4mA;
|
||||
break;
|
||||
case 2:
|
||||
si5351_drive = SIOUT_6mA;
|
||||
break;
|
||||
case 3:
|
||||
si5351_drive = SIOUT_8mA;
|
||||
break;
|
||||
default:
|
||||
si5351_drive = SIOUT_2mA;
|
||||
}
|
||||
|
||||
si5351_fast.setPower(si5351_drive, (uint8_t) clock);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,9 +23,6 @@ bool si5351_handler_init();
|
|||
bool si5351_set_frequency(si5351_clock_id clock, uint64_t frequency_hz_100);
|
||||
void si5351_output_enable(si5351_clock_id clock, bool enabled);
|
||||
void si5351_set_drive_strength(si5351_clock_id clock, uint8_t drive);
|
||||
bool si5351_set_frequency_fast(si5351_clock_id clock, uint64_t frequency_hz_100);
|
||||
void si5351_output_enable_fast(si5351_clock_id clock, bool enabled);
|
||||
void si5351_set_drive_strength_fast(si5351_clock_id clock, uint8_t drive);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "telemetry.h"
|
||||
#include "hal/system.h"
|
||||
#include "drivers/si4032/si4032.h"
|
||||
#include "drivers/ubxg6010/ubxg6010.h"
|
||||
#include "drivers/pulse_counter/pulse_counter.h"
|
||||
#include "bmp280_handler.h"
|
||||
|
@ -9,6 +8,14 @@
|
|||
#include "config.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifdef RS41
|
||||
#include "drivers/si4032/si4032.h"
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
#include "hal/clock_calibration.h"
|
||||
#include "drivers/si4063/si4063.h"
|
||||
#endif
|
||||
|
||||
// Initialize leap seconds with a known good value
|
||||
int8_t gps_time_leap_seconds = GPS_TIME_LEAP_SECONDS;
|
||||
|
||||
|
@ -20,7 +27,12 @@ void telemetry_collect(telemetry_data *data)
|
|||
|
||||
data->button_adc_value = system_get_button_adc_value();
|
||||
data->battery_voltage_millivolts = system_get_battery_voltage_millivolts();
|
||||
#ifdef RS41
|
||||
data->internal_temperature_celsius_100 = si4032_read_temperature_celsius_100();
|
||||
#endif
|
||||
#ifdef DFM17
|
||||
data->internal_temperature_celsius_100 = si4063_read_temperature_celsius_100();
|
||||
#endif
|
||||
|
||||
if (bmp280_enabled) {
|
||||
bmp280_read_telemetry(data);
|
||||
|
@ -60,6 +72,11 @@ void telemetry_collect(telemetry_data *data)
|
|||
data->gps.climb_cm_per_second = 0;
|
||||
}
|
||||
|
||||
#ifdef DFM17
|
||||
data->clock_calibration_trim = clock_calibration_get_trim();
|
||||
data->clock_calibration_count = clock_calibration_get_change_count();
|
||||
#endif
|
||||
|
||||
locator_from_lonlat(data->gps.longitude_degrees_1000000, data->gps.latitude_degrees_1000000,
|
||||
LOCATOR_PAIR_COUNT_FULL, data->locator);
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@ typedef struct _telemetry_data {
|
|||
gps_data gps;
|
||||
|
||||
char locator[LOCATOR_PAIR_COUNT_FULL * 2 + 1];
|
||||
|
||||
int clock_calibration_trim;
|
||||
uint16_t clock_calibration_count;
|
||||
} telemetry_data;
|
||||
|
||||
void telemetry_collect(telemetry_data *data);
|
||||
|
|
|
@ -111,6 +111,14 @@ size_t template_replace(char *dest, size_t dest_len, char *src, telemetry_data *
|
|||
strlcpy(temp, dest, dest_len);
|
||||
str_replace(dest, dest_len, temp, "$ri", replacement);
|
||||
|
||||
snprintf(replacement, sizeof(replacement), "%d", (int) data->clock_calibration_trim);
|
||||
strlcpy(temp, dest, dest_len);
|
||||
str_replace(dest, dest_len, temp, "$ct", replacement);
|
||||
|
||||
snprintf(replacement, sizeof(replacement), "%d", (int) data->clock_calibration_count);
|
||||
strlcpy(temp, dest, dest_len);
|
||||
str_replace(dest, dest_len, temp, "$cc", replacement);
|
||||
|
||||
free(temp);
|
||||
|
||||
return len;
|
||||
|
|
Ładowanie…
Reference in New Issue