mirror of
https://github.com/eddyem/stm32samples.git
synced 2026-05-07 13:26:56 +03:00
Compare commits
10 Commits
45559545ef
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4614183f38 | ||
|
|
0b7ecba54b | ||
|
|
f74ea14bbe | ||
|
|
40c8f23e36 | ||
| c93c2ac6ab | |||
|
|
56701d6ef6 | ||
|
|
9ef62c0d4d | ||
| 0e9c7361b9 | |||
|
|
8e591d73bd | ||
|
|
024d256563 |
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 18.0.2, 2026-03-18T23:46:41. -->
|
||||
<!-- Written by QtCreator 19.0.0, 2026-03-23T23:04:04. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
@@ -155,6 +155,7 @@
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
|
||||
<value type="QList<int>" key="Analyzer.Valgrind.VisibleErrorKinds"></value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
@@ -190,6 +191,7 @@
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
|
||||
<value type="QList<int>" key="Analyzer.Valgrind.VisibleErrorKinds"></value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
|
||||
11
F1:F103/AS3935-lightning/Makefile
Normal file
11
F1:F103/AS3935-lightning/Makefile
Normal file
@@ -0,0 +1,11 @@
|
||||
BINARY := as3935
|
||||
# MCU code
|
||||
MCU ?= F103x6
|
||||
# change this linking script depending on particular MCU model,
|
||||
LDSCRIPT ?= stm32f103x6.ld
|
||||
DEFINES := -DSTM32F10X_MD
|
||||
|
||||
include ../makefile.f1
|
||||
include ../../makefile.stm32
|
||||
|
||||
$(OBJDIR)/commproto.o: commproto.cpp $(VERSION_FILE)
|
||||
373
F1:F103/AS3935-lightning/Readme.md
Normal file
373
F1:F103/AS3935-lightning/Readme.md
Normal file
@@ -0,0 +1,373 @@
|
||||
# AS3935-based Lightning Detector
|
||||
|
||||
## Overview
|
||||
|
||||
This project implements a lightning detection system using two AS3935 Franklin Lightning Sensors interfaced
|
||||
with an STM32F103 microcontroller. The device communicates over USB CDC (virtual serial port) and exposes a
|
||||
text-based command protocol for configuration, monitoring, and data extraction.
|
||||
In the next version it is possible to add RS-232 or RS-485 as an alternative to USB.
|
||||
|
||||
Key features:
|
||||
- Dual AS3935 sensors on single SPI channel.
|
||||
- On-chip ADC for internal MCU temperature and voltage supply monitoring (more features may be added in the
|
||||
future).
|
||||
- Persistent configuration storage in internal Flash memory.
|
||||
- USB CDC interface with automatic detection of lightning events.
|
||||
- Extensive command set for real-time tuning and status readout.
|
||||
|
||||
---
|
||||
|
||||
## Hardware Connections
|
||||
|
||||
| Pin | Configuration | Function |
|
||||
|--------|-----------|----------|
|
||||
| PA0 | GPIO | INT0 - External interrupt from sensor 0 (pull-down) |
|
||||
| PA1 | GPIO | INT1 - External interrupt from sensor 1 (pull-down) |
|
||||
| PA2 | GPIO | CS0 - SPI chip select for sensor 0 |
|
||||
| PA3 | GPIO | CS1 - SPI chip select for sensor 1 |
|
||||
| PA5 | SPI1 SCK | SPI clock |
|
||||
| PA6 | SPI1 MISO | SPI Master In / Slave Out |
|
||||
| PA7 | SPI1 MOSI | SPI Master Out / Slave In |
|
||||
| PA9 | USART1 Tx | (unused in this version) |
|
||||
| PA10 | USART1 Rx | (unused) |
|
||||
| PA15 | GPIO | USB D+ pull-up control (1.5 kΩ) |
|
||||
| PC13 | GPIO | On-board LED (active low) |
|
||||
|
||||
The two AS3935 sensors share the SPI bus but have separate chip select lines and interrupt pins.
|
||||
If necessary, it can be expanded to include more sensors.
|
||||
|
||||
---
|
||||
|
||||
## USB CDC Communication
|
||||
|
||||
The device appears as a virtual COM port when connected to a host computer. Communication parameters (baud
|
||||
rate, parity, etc.) are ignored — the underlying USB bulk transport guarantees reliable delivery.
|
||||
|
||||
- **Line coding**: default sent by host (e.g., 115200 8N1) is accepted but not used.
|
||||
- **Ring buffers**: 1024 bytes each for TX and RX.
|
||||
- **Command terminator**: every command must end with a newline (`\n`). The response is also terminated by a
|
||||
newline, except for multi-line outputs (help, dumpconf, SPI).
|
||||
|
||||
When the host opens the virtual COM port (DTR asserted), the device becomes ready. If the port is closed, the
|
||||
`CDCready` flag is cleared and any outgoing data is discarded.
|
||||
|
||||
---
|
||||
|
||||
## Command Protocol
|
||||
|
||||
### General Syntax
|
||||
|
||||
```
|
||||
<command> [<channel>] [ = <value> ]
|
||||
```
|
||||
|
||||
- **command** — case-sensitive name (exactly as listed, all lowercase).
|
||||
- **channel** — numeric index of the sensor (0 or 1). Required for sensor-specific commands; omitted for
|
||||
global commands.
|
||||
- **value** — decimal integer, hexadecimal (`0x` prefix), binary (`0b` prefix), or a string (for commands
|
||||
like `setiface`). For the `SPI` command, the value is a sequence of hex bytes and/or quoted text.
|
||||
- **whitespaces** around `=` and between tokens is ignored.
|
||||
|
||||
There are two modes:
|
||||
|
||||
1. **Getter** — if `=` is absent, the current value is returned:
|
||||
```
|
||||
command<channel> = <current_value>
|
||||
```
|
||||
No `OK` response follows.
|
||||
|
||||
2. **Command** — also don't need `=`, but returns `OK` or error code.
|
||||
|
||||
3. **Setter** — if `=` is present, the value after `=` is assigned. The returned value is text describing
|
||||
error code (`OK` etc.). A getter-style response is **not** returned after a successful set; only `OK`
|
||||
appears.
|
||||
|
||||
For channel-dependent commands the response includes the channel number, e.g.:
|
||||
```
|
||||
gain0 = 5
|
||||
```
|
||||
|
||||
Global commands (no channel) use the same format without a number:
|
||||
```
|
||||
restonstart = 1
|
||||
```
|
||||
|
||||
Commands that are purely actions (e.g., `clearstat`, `resetdef`) only take a channel number and do not accept
|
||||
an `=` sign. On success they reply `OK`.
|
||||
|
||||
### Error Codes
|
||||
|
||||
If a command cannot be executed, one of the following error strings is returned instead of `OK`:
|
||||
|
||||
| Error String | Meaning |
|
||||
|--------------|---------|
|
||||
| `BADCMD` | Unknown command |
|
||||
| `BADPAR` | Invalid channel number |
|
||||
| `BADVAL` | Invalid value for the setter (out of range or malformed) |
|
||||
| `WRONGLEN` | Buffer overflow or line too long |
|
||||
| `CANTRUN` | SPI communication failure, sensor not responding, or illegal state |
|
||||
| `BUSY` | SPI or ring buffer busy - retry later |
|
||||
| `OVERFLOW` | Input string exceeded maximum length (256 characters) |
|
||||
|
||||
### Command Reference
|
||||
|
||||
All commands are defined in the source file `commproto.cpp` using macro `COMMAND_TABLE`.
|
||||
|
||||
#### Sensor Configuration Commands
|
||||
|
||||
These commands read or write parameters of a specific AS3935 sensor. They require the channel number as the
|
||||
first argument. For details, please refer to the sensor's technical documentation.
|
||||
|
||||
| Command | Description | Value Range | Default |
|
||||
|--------------|-------------|-------------|---------|
|
||||
| `displco` | Display on IRQ pin: 0=nothing, 1=TRCO, 2=SRCO, 3=LCO | 0 - 3 | 0 |
|
||||
| `gain` | Amplifier gain (AFE_GB) | 0 - 31 | 18 |
|
||||
| `lco_fdiv` | Antenna LCO frequency divider | 0 - 3 | 0 |
|
||||
| `maskdist` | Mask disturbers (1 = mask, 0 = allow) | 0 or 1 | 0 |
|
||||
| `minnumlig` | Minimum lightning number (0→1, 1→5, 2→9, 3→16) | 0 - 3 | 0 |
|
||||
| `nflev` | Noise floor level | 0 - 7 | 2 |
|
||||
| `srej` | Spike rejection | 0 - 15 | 2 |
|
||||
| `tuncap` | Tune capacitor setting (n·8 pF) | 0 - 15 | 0 |
|
||||
| `wdthres` | Watchdog threshold | 0 - 15 | 2 |
|
||||
|
||||
|
||||
**Syntax examples:**
|
||||
|
||||
```
|
||||
gain 0 = 12 set gain of sensor 0 to 12
|
||||
gain 1 read gain of sensor 1 → gain1 = 5
|
||||
displco 0 = 1 configure sensor 0 IRQ pin to output TRCO clock
|
||||
displco 0 read display mode → displco0 = 1
|
||||
```
|
||||
|
||||
#### Sensor Action & Status Commands
|
||||
|
||||
These commands do not take a value after `=`, only a channel number.
|
||||
|
||||
| Command | Description |
|
||||
|--------------|-------------|
|
||||
| `clearstat` | Clear the lightning statistics (last 15 min) |
|
||||
| `distance` | Read estimated distance to the lightning in km (0-63, 63=out of range) |
|
||||
| `energy` | Read energy of the last lightning (24-bit value) |
|
||||
| `intcode` | Read the last interrupt code: 1=Noise, 4=Disturber, 8=Lightning |
|
||||
| `iscalib` | Check if both RCO and TRCO calibrations are done (returns 1 if done) |
|
||||
| `resetdef` | Reset the sensor to factory default settings |
|
||||
| `wakeup` | Wake up the sensor and run RCO calibration |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```
|
||||
wakeup 0 wake up sensor 0, auto-calibrate → OK
|
||||
iscalib 0 read calibration status → iscalib0 = 1
|
||||
energy 0 read energy → energy0 = 123456
|
||||
distance 1 read distance → distance1 = 12
|
||||
intcode 0 read interrupt flags → intcode0 = 8
|
||||
clearstat 0 clear statistics → OK
|
||||
resetdef 0 reset to defaults → OK
|
||||
```
|
||||
|
||||
#### Global (Non-Sensor) Commands
|
||||
|
||||
These commands do not require a channel number.
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `dumpconf` | Dump the full current configuration (stored in RAM until saved) |
|
||||
| `eraseflash` | Erase the entire Flash configuration storage |
|
||||
| `help` | Print list of all commands and a repository URL |
|
||||
| `mcureset` | Software reset of the MCU |
|
||||
| `mcutemp` | MCU internal temperature in tenths of °C (e.g., 287 = 28.7℃) |
|
||||
| `readconf` | Read sensor configuration from the chip and update RAM for given channel |
|
||||
| `restonstart` | Restore sensor parameters at start-up (0 or 1) |
|
||||
| `saveconf` | Save the current RAM configuration to Flash |
|
||||
| `setiface` | Get/set the USB interface name string (max 16 characters) |
|
||||
| `time` | Current uptime in milliseconds |
|
||||
| `vdd` | Supply voltage VDD in hundredths of a volt (e.g., 330 = 3.30 V) |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```
|
||||
mcutemp → mcutemp = 287
|
||||
vdd → vdd = 330
|
||||
setiface = MyDetector set interface name to "MyDetector"
|
||||
setiface → setiface = MyDetector
|
||||
restonstart = 1 enable automatic restore at power-up
|
||||
saveconf save current configuration to Flash
|
||||
dumpconf print all stored and current parameters
|
||||
readconf 0 read sensor 0 parameters into RAM (for later save)
|
||||
```
|
||||
|
||||
#### SPI Low-Level Command
|
||||
|
||||
The `SPI` command allows arbitrary bidirectional SPI transactions to a selected sensor.
|
||||
|
||||
**Syntax:**
|
||||
```
|
||||
SPI <channel> = <hexdata>
|
||||
```
|
||||
|
||||
`<hexdata>` is a sequence of bytes expressed as space- or comma-separated hexadecimal numbers (without `0x`
|
||||
prefix), optionally with quoted text strings. Anything inside double quotes is transmitted as raw ASCII.
|
||||
|
||||
**Example:**
|
||||
```
|
||||
SPI 0 = 00 01 "hello" 02
|
||||
```
|
||||
This sends the bytes `0x00, 0x01, 'h','e','l','l','o', 0x02` to sensor 0 and returns the received bytes as a
|
||||
hex dump.
|
||||
|
||||
**Response format:**
|
||||
```
|
||||
SPI0 =
|
||||
00 01 68 65 6c 6c 6f 02
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Lightning Interrupts
|
||||
|
||||
The main loop continuously monitors the INT pins (PA0, PA1). When a rising edge is detected and the
|
||||
corresponding `DISPLCO` setting is `0` (not used as clock output), the code reads the interrupt register:
|
||||
|
||||
- Disturber and noise events are reported as:
|
||||
|
||||
```
|
||||
INTERRUPT0=NOICE,DISTURBER
|
||||
```
|
||||
- A lightning event additionally triggers `lightning_info()`, which prints the energy and distance:
|
||||
|
||||
```
|
||||
INTERRUPT0=LIGHTNING
|
||||
energy0 = 123456
|
||||
distance0 = 12
|
||||
```
|
||||
|
||||
If the pin is configured as a clock output (`displco` != 0), no interrupt processing occurs. You can mask
|
||||
disturber interrupts by command `maskdist n = 0`, in this case only `NOICE` and `LIGHTNING` will be
|
||||
monitored.
|
||||
|
||||
---
|
||||
|
||||
## Configuration Storage
|
||||
|
||||
User settings are stored in the internal Flash of the STM32F103. The Flash area reserved for configuration
|
||||
starts at a fixed address (defined in the linker script) and has a limited capacity. The structure
|
||||
`user_conf` contains:
|
||||
|
||||
- `userconf_sz` — magic cookie (size of the structure).
|
||||
- `iInterface` + `iIlength` — USB interface name (stored as two bytes per character for Unicode).
|
||||
- `spars[]` — per-sensor parameters (gain, WDTH, NF_LEV, SREJ, MIN_NUM_LIG, MASK_DIST, LCO_FDIV, TUN_CAP).
|
||||
- `flags.restore` — if 1, all parameters are automatically applied after reset.
|
||||
|
||||
**Mechanism:**
|
||||
|
||||
- Each time `saveconf` is issued, a new copy of the configuration is appended to the Flash storage. A binary
|
||||
search at boot finds the last valid entry.
|
||||
- If the storage becomes full, the next `saveconf` will first erase the entire storage and then write the
|
||||
current configuration.
|
||||
- `eraseflash` erases the whole configuration area; `restonstart` controls whether the stored configuration
|
||||
is uploaded to sensors at start-up.
|
||||
|
||||
**Capacity:**
|
||||
|
||||
Approximately `(FLASH_SIZE * 1024 - offset) / sizeof(user_conf)` configurations can be stored. For a typical
|
||||
64 KB device, this is hundreds of entries.
|
||||
|
||||
---
|
||||
|
||||
## ADC and Internal Sensors
|
||||
|
||||
The STM32’s ADC continuously samples the internal temperature sensor (channel 16) and the internal reference
|
||||
voltage (channel 17) in scan mode with DMA circular buffer. A median filter of 9 samples is applied to each
|
||||
channel.
|
||||
|
||||
- **`mcutemp`** returns temperature in tenths of °C, computed as:
|
||||
|
||||
`T = 25 + (V_25 - Vsense)/Avg_Slope`, with `V_25=1.45 V` and `Avg_Slope=4.3 mV/°C`.
|
||||
- **`vdd`** calculates the supply voltage using the internal 1.2 V reference:
|
||||
|
||||
`Vdd = 1.2 * 4096 / (ADC reading of Vrefint)`.
|
||||
|
||||
These commands do not require a sensor channel.
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Typical Workflow
|
||||
|
||||
1. Connect the device to a USB port and open a terminal at any baud rate.
|
||||
2. Type `help` to see the available commands.
|
||||
3. Wake up the sensors and calibrate:
|
||||
|
||||
```
|
||||
wakeup 0
|
||||
wakeup 1
|
||||
```
|
||||
4. Configure gains, thresholds and other values if need:
|
||||
|
||||
```
|
||||
gain 0 = 10
|
||||
wdthres 1 = 3
|
||||
```
|
||||
5. Verify settings:
|
||||
|
||||
```
|
||||
dumpconf
|
||||
```
|
||||
6. Enable persistent configuration:
|
||||
|
||||
```
|
||||
restonstart = 1
|
||||
saveconf
|
||||
```
|
||||
|
||||
7. Monitor lightning events — the device will print `INTERRUPT...` messages automatically. If need, you can
|
||||
clear statistics periodically or call other setters/getters; for example, reduce the sensitivity or
|
||||
lightning strike threshold during a severe thunderstorm and restore these values after it ends
|
||||
|
||||
### Reading Sensor Status
|
||||
Any time you can read sensor status (to get noice information or other data):
|
||||
|
||||
```
|
||||
intcode 0
|
||||
energy 0
|
||||
distance 0
|
||||
```
|
||||
|
||||
### Saving and Restoring Configuration
|
||||
|
||||
```
|
||||
dumpconf # see current settings
|
||||
saveconf # write to flash
|
||||
mcureset # reboot (only if something goes wrong)
|
||||
```
|
||||
|
||||
After reset, the configuration is automatically loaded (if `restonstart` was 1).
|
||||
|
||||
---
|
||||
|
||||
## How to distinguish between identical device
|
||||
|
||||
The device uses standard USB VID/PID, which are shared for free use by ST. Therefore, it may be difficult to
|
||||
determine which `/dev/ttyACMx` this particular device corresponds to.
|
||||
|
||||
You can set custom interface name (`setiface=...`). After storing in flash, reconnect or reset device and
|
||||
run `lsusb -v` for given device. At `iInterface` field you will see stored name. This field can be used to
|
||||
create human-readable symlinks in `/dev`.
|
||||
|
||||
For automatical creation of symlink in `/dev` to your `/dev/ttyACMx`, add this udev-script to
|
||||
`/etc/udev/rules.d/99-usbids.rules`
|
||||
|
||||
```
|
||||
ACTION=="add", DRIVERS=="usb", ENV{USB_IDS}="%s{idVendor}:%s{idProduct}"
|
||||
ACTION=="add", ENV{USB_IDS}=="0483:5740", ATTRS{interface}=="?*", PROGRAM="/bin/bash -c \"ls /dev | grep $attr{interface} | wc -l \"", SYMLINK+="$attr{interface}%c", MODE="0666", GROUP="tty"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build Information
|
||||
|
||||
Typing `help` you will see project repository URL, build number and build date at first string.
|
||||
|
||||
The code is distributed under GPLv3.
|
||||
111
F1:F103/AS3935-lightning/adc.c
Normal file
111
F1:F103/AS3935-lightning/adc.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* This file is part of the as3935 project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "adc.h"
|
||||
|
||||
uint16_t ADC_array[ADC_CHANNELS*9];
|
||||
|
||||
void adc_setup(){
|
||||
uint32_t ctr = 0;
|
||||
// Enable clocking
|
||||
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
|
||||
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
|
||||
__DSB();
|
||||
// DMA configuration
|
||||
DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR));
|
||||
DMA1_Channel1->CMAR = (uint32_t)(ADC_array);
|
||||
DMA1_Channel1->CNDTR = ADC_CHANNELS * 9;
|
||||
DMA1_Channel1->CCR = DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0
|
||||
| DMA_CCR_CIRC | DMA_CCR_PL | DMA_CCR_EN;
|
||||
RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_ADCPRE)) | RCC_CFGR_ADCPRE_DIV8; // ADC clock = RCC / 8
|
||||
// sampling time - 239.5 cycles for channels 0, 16 and 17
|
||||
ADC1->SMPR2 = ADC_SMPR2_SMP0;
|
||||
ADC1->SMPR1 = ADC_SMPR1_SMP16 | ADC_SMPR1_SMP17;
|
||||
// sequence order: 16[tsen] -> 17[vdd]
|
||||
ADC1->SQR3 = (16 << 0) | (17 << 5);
|
||||
ADC1->SQR1 = (ADC_CHANNELS - 1) << 20; // amount of conversions
|
||||
ADC1->CR1 = ADC_CR1_SCAN; // scan mode
|
||||
// DMA, continuous mode; enable vref & Tsens; enable SWSTART as trigger
|
||||
ADC1->CR2 = ADC_CR2_DMA | ADC_CR2_TSVREFE | ADC_CR2_CONT | ADC_CR2_EXTSEL | ADC_CR2_EXTTRIG;
|
||||
// wake up ADC
|
||||
ADC1->CR2 |= ADC_CR2_ADON;
|
||||
__DSB();
|
||||
// wait for Tstab - at least 1us
|
||||
IWDG->KR = IWDG_REFRESH;
|
||||
while(++ctr < 0xff) nop();
|
||||
// calibration
|
||||
ADC1->CR2 |= ADC_CR2_RSTCAL;
|
||||
ctr = 0; while((ADC1->CR2 & ADC_CR2_RSTCAL) && ++ctr < 0xfffff) IWDG->KR = IWDG_REFRESH;
|
||||
ADC1->CR2 |= ADC_CR2_CAL;
|
||||
ctr = 0; while((ADC1->CR2 & ADC_CR2_CAL) && ++ctr < 0xfffff) IWDG->KR = IWDG_REFRESH;
|
||||
// clear possible errors and start
|
||||
ADC1->SR = 0;
|
||||
ADC1->CR2 |= ADC_CR2_SWSTART;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief getADCval - calculate median value for `nch` channel
|
||||
* @param nch - number of channel
|
||||
* @return
|
||||
*/
|
||||
uint16_t getADCval(int nch){
|
||||
int i, addr = nch;
|
||||
#define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); }
|
||||
#define PIX_SWAP(a,b) { register uint16_t temp=(a);(a)=(b);(b)=temp; }
|
||||
uint16_t p[9];
|
||||
for(i = 0; i < 9; ++i, addr += ADC_CHANNELS){ // first we should prepare array for optmed
|
||||
p[i] = ADC_array[addr];
|
||||
}
|
||||
PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
|
||||
PIX_SORT(p[0], p[1]) ; PIX_SORT(p[3], p[4]) ; PIX_SORT(p[6], p[7]) ;
|
||||
PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
|
||||
PIX_SORT(p[0], p[3]) ; PIX_SORT(p[5], p[8]) ; PIX_SORT(p[4], p[7]) ;
|
||||
PIX_SORT(p[3], p[6]) ; PIX_SORT(p[1], p[4]) ; PIX_SORT(p[2], p[5]) ;
|
||||
PIX_SORT(p[4], p[7]) ; PIX_SORT(p[4], p[2]) ; PIX_SORT(p[6], p[4]) ;
|
||||
PIX_SORT(p[4], p[2]) ;
|
||||
return p[4];
|
||||
#undef PIX_SORT
|
||||
#undef PIX_SWAP
|
||||
}
|
||||
|
||||
// get voltage @input nch (1/100V)
|
||||
uint32_t getADCvoltage(int nch){
|
||||
uint32_t v = getADCval(nch);
|
||||
v *= getVdd();
|
||||
v /= 0xfff; // 12bit ADC
|
||||
return v;
|
||||
}
|
||||
|
||||
// return MCU temperature (degrees of celsius * 10)
|
||||
int32_t getMCUtemp(){
|
||||
// Temp = (V25 - Vsense)/Avg_Slope + 25
|
||||
// V_25 = 1.45V, Slope = 4.3e-3
|
||||
int32_t Vsense = getVdd() * getADCval(ADC_CH_TSEN);
|
||||
int32_t temperature = 593920 - Vsense; // 593920 == 145*4096
|
||||
temperature /= 172; // == /(4096*10*4.3e-3), 10 - to convert from *100 to *10
|
||||
temperature += 250;
|
||||
return(temperature);
|
||||
}
|
||||
|
||||
// return Vdd * 100 (V)
|
||||
uint32_t getVdd(){
|
||||
uint32_t vdd = 120 * 4096; // 1.2V
|
||||
vdd /= getADCval(ADC_CH_VDD);
|
||||
return vdd;
|
||||
}
|
||||
41
F1:F103/AS3935-lightning/adc.h
Normal file
41
F1:F103/AS3935-lightning/adc.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This file is part of the as3935 project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stm32f1.h>
|
||||
|
||||
// ADC channels in array
|
||||
enum{
|
||||
ADC_CH_TSEN, // T sensor
|
||||
ADC_CH_VDD, // Vdd sensor
|
||||
ADC_CHANNELS
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief ADC_array - array for ADC channels with median filtering
|
||||
*/
|
||||
extern uint16_t ADC_array[];
|
||||
|
||||
void adc_setup();
|
||||
int32_t getMCUtemp();
|
||||
uint32_t getVdd();
|
||||
uint16_t getADCval(int nch);
|
||||
uint32_t getADCvoltage(int nch);
|
||||
|
||||
BIN
F1:F103/AS3935-lightning/as3935.bin
Executable file
BIN
F1:F103/AS3935-lightning/as3935.bin
Executable file
Binary file not shown.
291
F1:F103/AS3935-lightning/as3935.c
Normal file
291
F1:F103/AS3935-lightning/as3935.c
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* This file is part of the as3935 project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "as3935.h"
|
||||
#include "spi.h"
|
||||
|
||||
#define MODE_READ (1 << 6)
|
||||
#define MODE_WRITE (0)
|
||||
#define MODE_MASK (0x3f)
|
||||
|
||||
extern uint32_t Tms;
|
||||
|
||||
// read one register
|
||||
int as3935_read(uint8_t reg, uint8_t *data){
|
||||
uint8_t word[2];
|
||||
word[0] = MODE_READ | (reg & MODE_MASK);
|
||||
word[1] = 0;
|
||||
if(0 == SPI_transmit(word, 2)) return FALSE;
|
||||
if(data) *data = word[1];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int as3935_write(uint8_t reg, uint8_t data){
|
||||
uint8_t word[2];
|
||||
word[0] = MODE_WRITE | (reg & MODE_MASK);
|
||||
word[1] = data;
|
||||
if(0 == SPI_transmit(word, 2)) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int as3935_get_displco(uint8_t *n){
|
||||
t_tun_disp t;
|
||||
if(!as3935_read(TUN_DISP, &t.u8)) return FALSE; // we need to save old `tun_cap` value
|
||||
*n = t.DISP_TRCO + 2 * t.DISP_SRCO + 3 * t.DISP_LCO;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
// display on IRQ: nothing (0), TRCO (1), SRCO (2) or LCO (3)
|
||||
int as3935_displco(uint8_t n){
|
||||
if(n > 3) return FALSE;
|
||||
t_tun_disp t;
|
||||
if(!as3935_read(TUN_DISP, &t.u8)) return FALSE; // we need to save old `tun_cap` value
|
||||
t.DISP_LCO = t.DISP_SRCO = t.DISP_TRCO = 0;
|
||||
switch(n){
|
||||
case 1:
|
||||
t.DISP_TRCO = 1;
|
||||
break;
|
||||
case 2:
|
||||
t.DISP_SRCO = 1;
|
||||
break;
|
||||
case 3:
|
||||
t.DISP_LCO = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return as3935_write(TUN_DISP, t.u8);
|
||||
}
|
||||
|
||||
// tune LCO: change capasitor value
|
||||
int as3935_tuncap(uint8_t n){
|
||||
if(n > 0xf) return FALSE;
|
||||
t_tun_disp t;
|
||||
if(!as3935_read(TUN_DISP, &t.u8)) return FALSE;
|
||||
t.TUN_CAP = n;
|
||||
return as3935_write(TUN_DISP, t.u8);
|
||||
}
|
||||
|
||||
int as3935_get_tuncap(uint8_t *n){
|
||||
t_tun_disp t;
|
||||
if(!as3935_read(TUN_DISP, &t.u8)) return FALSE;
|
||||
*n = t.TUN_CAP;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// set gain
|
||||
int as3935_gain(uint8_t n){
|
||||
if(n > 0x1f) return FALSE;
|
||||
t_afe_gain g;
|
||||
if(!as3935_read(AFE_GAIN, &g.u8)) return FALSE;
|
||||
g.AFE_GB = n;
|
||||
return as3935_write(AFE_GAIN, g.u8);
|
||||
}
|
||||
#endif
|
||||
|
||||
// calibrate RCO
|
||||
int as3935_calib_rco(){
|
||||
t_tun_disp t;
|
||||
if(!as3935_read(TUN_DISP, &t.u8)) return FALSE;
|
||||
if(!as3935_write(CALIB_RCO, DIRECT_COMMAND)) return FALSE;
|
||||
t.DISP_LCO = t.DISP_TRCO = 0;
|
||||
t.DISP_SRCO = 1;
|
||||
if(!as3935_write(TUN_DISP, t.u8)) return FALSE;
|
||||
uint32_t Tstart = Tms;
|
||||
while(Tms - Tstart < 3) IWDG->KR = IWDG_REFRESH; // sleep for ~2ms
|
||||
t.DISP_SRCO = 0;
|
||||
if(!as3935_write(TUN_DISP, t.u8)) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int as3935_get_calib(uint8_t *n){
|
||||
t_calib srco, trco;
|
||||
if(!as3935_read(CALIB_TRCO, &trco.u8)) return FALSE;
|
||||
if(!as3935_read(CALIB_SRCO, &srco.u8)) return FALSE;
|
||||
*n = (!srco.CALIB_DONE || !trco.CALIB_DONE) ? 0 : 1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// wakeup - call this function after first run
|
||||
int as3935_wakeup(){
|
||||
t_afe_gain g;
|
||||
if(!as3935_read(AFE_GAIN, &g.u8)) return FALSE;
|
||||
g.PWD = 0;
|
||||
if(!as3935_write(AFE_GAIN, g.u8)) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// set amplifier gain
|
||||
int as3935_gain(uint8_t g){
|
||||
if(g > 0x1f) return FALSE;
|
||||
t_afe_gain a = {0};
|
||||
a.AFE_GB = g;
|
||||
return as3935_write(AFE_GAIN, a.u8);
|
||||
}
|
||||
|
||||
int as3935_get_gain(uint8_t *n){
|
||||
t_afe_gain g;
|
||||
if(!as3935_read(AFE_GAIN, &g.u8)) return FALSE;
|
||||
*n = g.AFE_GB;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// watchdog threshold
|
||||
int as3935_wdthres(uint8_t t){
|
||||
if(t > 0x0f) return FALSE;
|
||||
t_threshold thres;
|
||||
if(!as3935_read(THRESHOLD, &thres.u8)) return FALSE;
|
||||
thres.WDTH = t;
|
||||
return as3935_write(THRESHOLD, thres.u8);
|
||||
}
|
||||
|
||||
int as3935_get_wdthres(uint8_t *n){
|
||||
t_threshold thres;
|
||||
if(!as3935_read(THRESHOLD, &thres.u8)) return FALSE;
|
||||
*n = thres.WDTH;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// noice floor level
|
||||
int as3935_nflev(uint8_t l){
|
||||
if(l > 7) return FALSE;
|
||||
t_threshold thres;
|
||||
if(!as3935_read(THRESHOLD, &thres.u8)) return FALSE;
|
||||
thres.NF_LEV = l;
|
||||
return as3935_write(THRESHOLD, thres.u8);
|
||||
}
|
||||
|
||||
int as3935_get_nflev(uint8_t *n){
|
||||
t_threshold thres;
|
||||
if(!as3935_read(THRESHOLD, &thres.u8)) return FALSE;
|
||||
*n = thres.NF_LEV;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// spike rejection
|
||||
int as3935_srej(uint8_t s){
|
||||
if(s > 0xf) return FALSE;
|
||||
t_lightning_reg lr;
|
||||
if(!as3935_read(LIGHTNING_REG, &lr.u8)) return FALSE;
|
||||
lr.SREJ = s;
|
||||
return as3935_write(LIGHTNING_REG, lr.u8);
|
||||
}
|
||||
|
||||
int as3935_get_srej(uint8_t *n){
|
||||
t_lightning_reg lr;
|
||||
if(!as3935_read(LIGHTNING_REG, &lr.u8)) return FALSE;
|
||||
*n = lr.SREJ;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// minimal lighting number
|
||||
int as3935_minnumlig(uint8_t n){
|
||||
if(n > 3) return FALSE;
|
||||
t_lightning_reg lr;
|
||||
if(!as3935_read(LIGHTNING_REG, &lr.u8)) return FALSE;
|
||||
lr.MIN_NUM_LIG = n;
|
||||
return as3935_write(LIGHTNING_REG, lr.u8);
|
||||
}
|
||||
|
||||
int as3935_get_minnumlig(uint8_t *n){
|
||||
t_lightning_reg lr;
|
||||
if(!as3935_read(LIGHTNING_REG, &lr.u8)) return FALSE;
|
||||
*n = lr.MIN_NUM_LIG;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// clear amount of lightnings for last 15 min
|
||||
int as3935_clearstat(){
|
||||
t_lightning_reg lr;
|
||||
if(!as3935_read(LIGHTNING_REG, &lr.u8)) return FALSE;
|
||||
lr.CL_STAT = 1;
|
||||
return as3935_write(LIGHTNING_REG, lr.u8);
|
||||
}
|
||||
|
||||
// get interrupt code
|
||||
int as3935_intcode(uint8_t *code){
|
||||
if(!code) return FALSE;
|
||||
t_int_mask_ant i;
|
||||
if(!as3935_read(INT_MASK_ANT, &i.u8)) return FALSE;
|
||||
*code = i.INT;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// should interrupt react on disturbers?
|
||||
int as3935_maskdist(uint8_t m){
|
||||
if(m > 1) return FALSE;
|
||||
t_int_mask_ant i;
|
||||
if(!as3935_read(INT_MASK_ANT, &i.u8)) return FALSE;
|
||||
i.MASK_DIST = m;
|
||||
return as3935_write(INT_MASK_ANT, i.u8);
|
||||
}
|
||||
|
||||
int as3935_get_maskdist(uint8_t *n){
|
||||
t_int_mask_ant i;
|
||||
if(!as3935_read(INT_MASK_ANT, &i.u8)) return FALSE;
|
||||
*n = i.MASK_DIST;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// set Fdiv of antenna LCO
|
||||
int as3935_lco_fdiv(uint8_t d){
|
||||
if(d > 3) return FALSE;
|
||||
t_int_mask_ant i;
|
||||
if(!as3935_read(INT_MASK_ANT, &i.u8)) return FALSE;
|
||||
i.LCO_FDIV = d;
|
||||
return as3935_write(INT_MASK_ANT, i.u8);
|
||||
}
|
||||
|
||||
int as3935_get_lco_fdiv(uint8_t *n){
|
||||
t_int_mask_ant i;
|
||||
if(!as3935_read(INT_MASK_ANT, &i.u8)) return FALSE;
|
||||
*n = i.LCO_FDIV;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// calculate last lightning energy
|
||||
int as3935_energy(uint32_t *E){
|
||||
if(!E) return FALSE;
|
||||
uint8_t u; uint32_t energy;
|
||||
t_s_lig_mm mm;
|
||||
if(!as3935_read(S_LIG_MM, &mm.u8)) return FALSE;
|
||||
energy = mm.S_LIG_MM << 8;
|
||||
if(!as3935_read(S_LIG_M, &u)) return FALSE;
|
||||
energy |= u;
|
||||
energy <<= 8;
|
||||
if(!as3935_read(S_LIG_L, &u)) return FALSE;
|
||||
energy |= u;
|
||||
*E = energy;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// get distance
|
||||
int as3935_distance(uint8_t *d){
|
||||
if(!d) return FALSE;
|
||||
t_distance dist;
|
||||
if(!as3935_read(DISTANCE, &dist.u8)) return FALSE;
|
||||
*d = dist.DISTANCE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// reset to factory settings
|
||||
int as3935_resetdef(){
|
||||
return as3935_write(PRESET_DEFAULT, DIRECT_COMMAND);
|
||||
}
|
||||
1
F1:F103/AS3935-lightning/as3935.cflags
Normal file
1
F1:F103/AS3935-lightning/as3935.cflags
Normal file
@@ -0,0 +1 @@
|
||||
-std=c17
|
||||
6
F1:F103/AS3935-lightning/as3935.config
Normal file
6
F1:F103/AS3935-lightning/as3935.config
Normal file
@@ -0,0 +1,6 @@
|
||||
#define EBUG
|
||||
#define STM32F1
|
||||
#define STM32F103x8
|
||||
#define STM32F10X_MD
|
||||
#define BUILD_NUMBER 1
|
||||
#define BUILD_DATE "xx"
|
||||
1
F1:F103/AS3935-lightning/as3935.creator
Normal file
1
F1:F103/AS3935-lightning/as3935.creator
Normal file
@@ -0,0 +1 @@
|
||||
[General]
|
||||
219
F1:F103/AS3935-lightning/as3935.creator.user
Normal file
219
F1:F103/AS3935-lightning/as3935.creator.user
Normal file
@@ -0,0 +1,219 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 19.0.1, 2026-04-25T20:44:46. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
<value type="QByteArray">{7bd84e39-ca37-46d3-be9d-99ebea85bc0d}</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||
<value type="qlonglong">0</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="bool" key="EditorConfiguration.AutoDetect">true</value>
|
||||
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||
<value type="QString" key="language">Cpp</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||
<value type="QString" key="language">QmlJS</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
|
||||
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.LineEndingBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
|
||||
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
|
||||
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
|
||||
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
||||
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
|
||||
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
|
||||
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
|
||||
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
||||
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanIndentation">false</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
|
||||
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
|
||||
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
|
||||
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
|
||||
<value type="bool" key="AutoTest.Framework.Boost">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.CTest">false</value>
|
||||
<value type="bool" key="AutoTest.Framework.Catch">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.GTest">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
|
||||
</valuemap>
|
||||
<value type="bool" key="AutoTest.ApplyFilter">false</value>
|
||||
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
|
||||
<valuelist type="QVariantList" key="AutoTest.PathFilters"/>
|
||||
<value type="int" key="AutoTest.RunAfterBuild">0</value>
|
||||
<value type="bool" key="AutoTest.UseGlobal">true</value>
|
||||
<valuemap type="QVariantMap" key="ClangTools">
|
||||
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
|
||||
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
|
||||
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
|
||||
<value type="int" key="ClangTools.ParallelJobs">8</value>
|
||||
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
||||
</valuemap>
|
||||
<value type="int" key="RcSync">0</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="QString" key="DeviceType">Desktop</value>
|
||||
<value type="bool" key="HasPerBcDcs">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{65a14f9e-e008-4c1b-89df-4eaa4774b6e3}</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
|
||||
<value type="QString">all</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
|
||||
<value type="QString">clean</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Default</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
|
||||
<value type="QList<int>" key="Analyzer.Valgrind.VisibleErrorKinds"></value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
|
||||
<value type="QList<int>" key="Analyzer.Valgrind.VisibleErrorKinds"></value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="qlonglong">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>Version</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
</qtcreator>
|
||||
1
F1:F103/AS3935-lightning/as3935.cxxflags
Normal file
1
F1:F103/AS3935-lightning/as3935.cxxflags
Normal file
@@ -0,0 +1 @@
|
||||
-std=c++17
|
||||
28
F1:F103/AS3935-lightning/as3935.files
Normal file
28
F1:F103/AS3935-lightning/as3935.files
Normal file
@@ -0,0 +1,28 @@
|
||||
Readme.md
|
||||
adc.c
|
||||
adc.h
|
||||
as3935.c
|
||||
as3935.h
|
||||
bissC.c
|
||||
bissC.h
|
||||
commproto.cpp
|
||||
commproto.h
|
||||
flash.c
|
||||
flash.h
|
||||
hardware.c
|
||||
hardware.h
|
||||
main.c
|
||||
ringbuffer.c
|
||||
ringbuffer.h
|
||||
spi.c
|
||||
spi.h
|
||||
strfunc.c
|
||||
strfunc.h
|
||||
usart.c
|
||||
usart.h
|
||||
usb_descr.c
|
||||
usb_descr.h
|
||||
usb_lib.c
|
||||
usb_lib.h
|
||||
usb_dev.c
|
||||
usb_dev.h
|
||||
153
F1:F103/AS3935-lightning/as3935.h
Normal file
153
F1:F103/AS3935-lightning/as3935.h
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* This file is part of the as3935 project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum AS3935_REGISTERS{
|
||||
AFE_GAIN = 0x00,
|
||||
THRESHOLD,
|
||||
LIGHTNING_REG,
|
||||
INT_MASK_ANT,
|
||||
S_LIG_L,
|
||||
S_LIG_M,
|
||||
S_LIG_MM,
|
||||
DISTANCE,
|
||||
TUN_DISP,
|
||||
CALIB_TRCO = 0x3A,
|
||||
CALIB_SRCO = 0x3B,
|
||||
PRESET_DEFAULT = 0x3C,
|
||||
CALIB_RCO = 0x3D
|
||||
};
|
||||
|
||||
// REGISTERS
|
||||
|
||||
typedef union{
|
||||
struct{
|
||||
uint8_t PWD : 1;
|
||||
uint8_t AFE_GB : 5;
|
||||
uint8_t RESERVED : 2;
|
||||
}; uint8_t u8;
|
||||
} t_afe_gain;
|
||||
|
||||
typedef union{
|
||||
struct{
|
||||
uint8_t WDTH : 4;
|
||||
uint8_t NF_LEV : 3;
|
||||
uint8_t RESERVED : 1;
|
||||
}; uint8_t u8;
|
||||
} t_threshold;
|
||||
|
||||
typedef union{
|
||||
struct{
|
||||
uint8_t SREJ : 4;
|
||||
// minimal number of lightnings
|
||||
#define NUM_LIG_1 (0)
|
||||
#define NUM_LIG_5 (1)
|
||||
#define NUM_LIG_9 (2)
|
||||
#define NUM_LIG_16 (3)
|
||||
uint8_t MIN_NUM_LIG : 2;
|
||||
uint8_t CL_STAT : 1;
|
||||
uint8_t RESERVED : 1;
|
||||
}; uint8_t u8;
|
||||
} t_lightning_reg;
|
||||
|
||||
typedef union{
|
||||
struct{
|
||||
// interrupt flags
|
||||
// noice level too high
|
||||
#define INT_NH (1)
|
||||
// disturber detected
|
||||
#define INT_D (4)
|
||||
// lightning interrupt
|
||||
#define INT_L (8)
|
||||
uint8_t INT : 4;
|
||||
uint8_t RESERVED : 1;
|
||||
uint8_t MASK_DIST : 1;
|
||||
uint8_t LCO_FDIV : 2;
|
||||
}; uint8_t u8;
|
||||
} t_int_mask_ant;
|
||||
|
||||
typedef union{
|
||||
struct{
|
||||
uint8_t S_LIG_MM : 5;
|
||||
uint8_t RESERVED : 3;
|
||||
}; uint8_t u8;
|
||||
} t_s_lig_mm;
|
||||
|
||||
typedef union{
|
||||
struct{
|
||||
uint8_t DISTANCE : 6;
|
||||
uint8_t RESERVED : 2;
|
||||
}; uint8_t u8;
|
||||
} t_distance;
|
||||
|
||||
typedef union{
|
||||
struct{
|
||||
uint8_t TUN_CAP : 4;
|
||||
uint8_t RESERVED : 1;
|
||||
uint8_t DISP_TRCO : 1;
|
||||
uint8_t DISP_SRCO : 1;
|
||||
uint8_t DISP_LCO : 1;
|
||||
}; uint8_t u8;
|
||||
} t_tun_disp;
|
||||
|
||||
typedef union{
|
||||
struct{
|
||||
uint8_t RESERVED : 6;
|
||||
uint8_t CALIB_NOK : 1;
|
||||
uint8_t CALIB_DONE : 1;
|
||||
}; uint8_t u8;
|
||||
} t_calib;
|
||||
|
||||
// direct command send to PRESET_DEFAULT and CALIB_RCO
|
||||
#define DIRECT_COMMAND (0x96)
|
||||
// distance out of range
|
||||
#define DIST_OUT_OF_RANGE (0x3f)
|
||||
|
||||
int as3935_read(uint8_t reg, uint8_t *data);
|
||||
int as3935_write(uint8_t reg, uint8_t data);
|
||||
int as3935_wakeup();
|
||||
int as3935_get_calib(uint8_t *n);
|
||||
int as3935_calib_rco();
|
||||
|
||||
int as3935_displco(uint8_t n);
|
||||
int as3935_get_displco(uint8_t *n);
|
||||
int as3935_tuncap(uint8_t n);
|
||||
int as3935_get_tuncap(uint8_t *n);
|
||||
int as3935_gain(uint8_t n);
|
||||
int as3935_get_gain(uint8_t *n);
|
||||
//int as3935_set_gain(uint8_t g);
|
||||
int as3935_wdthres(uint8_t t);
|
||||
int as3935_get_wdthres(uint8_t *t);
|
||||
int as3935_nflev(uint8_t l);
|
||||
int as3935_get_nflev(uint8_t *l);
|
||||
int as3935_srej(uint8_t s);
|
||||
int as3935_get_srej(uint8_t *s);
|
||||
int as3935_minnumlig(uint8_t n);
|
||||
int as3935_get_minnumlig(uint8_t *n);
|
||||
int as3935_clearstat();
|
||||
int as3935_intcode(uint8_t *code);
|
||||
int as3935_maskdist(uint8_t m);
|
||||
int as3935_get_maskdist(uint8_t *m);
|
||||
int as3935_lco_fdiv(uint8_t d);
|
||||
int as3935_get_lco_fdiv(uint8_t *d);
|
||||
int as3935_energy(uint32_t *E);
|
||||
int as3935_distance(uint8_t *d);
|
||||
int as3935_resetdef();
|
||||
6
F1:F103/AS3935-lightning/as3935.includes
Normal file
6
F1:F103/AS3935-lightning/as3935.includes
Normal file
@@ -0,0 +1,6 @@
|
||||
.
|
||||
../inc
|
||||
../inc/Fx
|
||||
../inc/cm
|
||||
../inc/ld
|
||||
../inc/startup
|
||||
467
F1:F103/AS3935-lightning/commproto.cpp
Normal file
467
F1:F103/AS3935-lightning/commproto.cpp
Normal file
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
* This file is part of the as3935 project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
extern "C"{
|
||||
#include <stm32f1.h>
|
||||
|
||||
#include "adc.h"
|
||||
#include "as3935.h"
|
||||
#include "commproto.h"
|
||||
#include "flash.h"
|
||||
#include "hardware.h"
|
||||
#include "spi.h"
|
||||
#include "strfunc.h"
|
||||
}
|
||||
|
||||
// sending function
|
||||
static int (*SEND)(const char *str) = nullptr;
|
||||
|
||||
extern volatile uint32_t Tms;
|
||||
|
||||
static uint8_t curbuf[MAXSTRLEN];
|
||||
|
||||
// COMMAND(USART, "Read USART data or send (USART=hex)")
|
||||
|
||||
// list of all commands and handlers
|
||||
#define COMMAND_TABLE \
|
||||
COMMAND(clearstat, "clear amount of lightnings for last 15 min") \
|
||||
COMMAND(displco, "display on LCO: 0- nothing, 1- TRCO, 2- SRCO, 3- LCO") \
|
||||
COMMAND(distance, "distance to lightning, km") \
|
||||
COMMAND(dumpconf, "dump current configuration") \
|
||||
COMMAND(energy, "energy of last lightning") \
|
||||
COMMAND(eraseflash, "erase full flash storage") \
|
||||
COMMAND(gain, "change sensor's gain (0..1f)") \
|
||||
COMMAND(help, "show this help") \
|
||||
COMMAND(intcode, "last interrupt code") \
|
||||
COMMAND(iscalib, "check if sensor x calibrated") \
|
||||
COMMAND(lco_fdiv, "set Fdiv of antenna LCO") \
|
||||
COMMAND(maskdist, "mask (1) or unmask (0) disturber") \
|
||||
COMMAND(mcutemp, "get MCU temperature (degC*10)") \
|
||||
COMMAND(mcureset, "reset MCU") \
|
||||
COMMAND(minnumlig, "ninimal lightnings number (0..3 -> 1/5/9/16)") \
|
||||
COMMAND(nflev, "noice floor level (0..7)") \
|
||||
COMMAND(readconf, "read configuration from given sensor") \
|
||||
COMMAND(resetdef, "reset sensor to defaults") \
|
||||
COMMAND(restonstart,"restore sensors configuration on start") \
|
||||
COMMAND(saveconf, "save current configuration into flash") \
|
||||
COMMAND(setiface, "set USB interface name (max 16 symbols)") \
|
||||
COMMAND(SPI, "transfer SPI data: SPIx=data (hex)") \
|
||||
COMMAND(srej, "spike rejection (0..15)") \
|
||||
COMMAND(time, "show current time (ms)") \
|
||||
COMMAND(tuncap, "set 'tune capasitors' to given value (0..15)") \
|
||||
COMMAND(vdd, "get approx Vdd value (V*100)") \
|
||||
COMMAND(wakeup, "wake-up given sensor and make its calibration") \
|
||||
COMMAND(wdthres, "watchdog threshold (0..15)" )
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
} CmdInfo;
|
||||
|
||||
// prototypes
|
||||
#define COMMAND(name, desc) static errcodes_t cmd_ ## name(const char*, char*);
|
||||
COMMAND_TABLE
|
||||
#undef COMMAND
|
||||
|
||||
static const CmdInfo cmdInfo[] = { // command name, description - for `help`
|
||||
#define COMMAND(name, desc) { #name, desc },
|
||||
COMMAND_TABLE
|
||||
#undef COMMAND
|
||||
};
|
||||
|
||||
static const char* errtxt[ERR_AMOUNT] = {
|
||||
[ERR_OK] = "OK\n",
|
||||
[ERR_BADCMD] = "BADCMD\n",
|
||||
[ERR_BADPAR] = "BADPAR\n",
|
||||
[ERR_BADVAL] = "BADVAL\n",
|
||||
[ERR_WRONGLEN] = "WRONGLEN\n",
|
||||
[ERR_CANTRUN] = "CANTRUN\n",
|
||||
[ERR_BUSY] = "BUSY\n",
|
||||
[ERR_OVERFLOW] = "OVERFLOW\n",
|
||||
};
|
||||
|
||||
const char *EQ = " = "; // equal sign for getters
|
||||
|
||||
// send `command = `
|
||||
#define CMDEQ() do{SEND(cmd); SEND(EQ);}while(0)
|
||||
// send `commandXXX = `
|
||||
#define CMDEQP(x) do{SEND(cmd); SEND(u2str((uint32_t)x)); SEND(EQ);}while(0)
|
||||
|
||||
|
||||
/**
|
||||
* @brief splitargs - get command parameter and setter from `args`
|
||||
* @param args (i) - rest of string after command (like `1 = PU OD OUT`)
|
||||
* @param parno (o) - parameter number or -1 if none
|
||||
* @return setter (part after `=` without leading spaces) or NULL if none
|
||||
*/
|
||||
static char *splitargs(char *args, int32_t *parno){
|
||||
if(!args) return NULL;
|
||||
uint32_t U32;
|
||||
char *next = getnum(args, &U32);
|
||||
int p = -1;
|
||||
if(next != args && U32 <= MAXPARNO) p = U32;
|
||||
if(parno) *parno = p;
|
||||
next = strchr(next, '=');
|
||||
if(next){
|
||||
if(*(++next)) next = omit_spaces(next);
|
||||
if(*next == 0) next = NULL;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief argsvals - split `args` into `parno` and setter's value
|
||||
* @param args - rest of string after command
|
||||
* @param parno (o) - parameter number or -1 if none
|
||||
* @param parval - integer setter's value
|
||||
* @return false if no setter or it's not a number, true - got setter's num
|
||||
*/
|
||||
static bool argsvals(char *args, int32_t *parno, int32_t *parval){
|
||||
char *setter = splitargs(args, parno);
|
||||
if(!setter) return false;
|
||||
int32_t I32;
|
||||
char *next = getint(setter, &I32);
|
||||
if(next != setter && parval){
|
||||
*parval = I32;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_time(const char *cmd, char*){
|
||||
CMDEQ();
|
||||
SEND(u2str(Tms)); SEND("\n");
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_mcureset(const char*, char*){
|
||||
NVIC_SystemReset();
|
||||
return ERR_CANTRUN; // never reached
|
||||
}
|
||||
|
||||
static errcodes_t cmd_saveconf(const char*, char*){
|
||||
if(store_userconf()) return ERR_CANTRUN;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_eraseflash(const char*, char*){
|
||||
if(erase_storage(-1)) return ERR_CANTRUN;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_readconf(const char*, char* args){
|
||||
int32_t CHno = -1;
|
||||
splitargs(args, &CHno);
|
||||
if(CHno < 0 || CHno >= SENSORS_AMOUNT) return ERR_BADPAR;
|
||||
as3935_channel = static_cast<uint8_t>(CHno);
|
||||
uint8_t par;
|
||||
if(!as3935_get_gain(&par)) return ERR_CANTRUN;
|
||||
the_conf.spars[CHno].AFE_GB = par;
|
||||
if(!as3935_get_wdthres(&par)) return ERR_CANTRUN;
|
||||
the_conf.spars[CHno].WDTH = par;
|
||||
if(!as3935_get_nflev(&par)) return ERR_CANTRUN;
|
||||
the_conf.spars[CHno].NF_LEV = par;
|
||||
if(!as3935_get_srej(&par)) return ERR_CANTRUN;
|
||||
the_conf.spars[CHno].SREJ = par;
|
||||
if(!as3935_get_minnumlig(&par)) return ERR_CANTRUN;
|
||||
the_conf.spars[CHno].MIN_NUM_LIG = par;
|
||||
if(!as3935_get_maskdist(&par)) return ERR_CANTRUN;
|
||||
the_conf.spars[CHno].MASK_DIST = par;
|
||||
if(!as3935_get_lco_fdiv(&par)) return ERR_CANTRUN;
|
||||
the_conf.spars[CHno].LCO_FDIV = par;
|
||||
if(!as3935_get_tuncap(&par)) return ERR_CANTRUN;
|
||||
the_conf.spars[CHno].TUN_CAP = par;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_setiface(const char* cmd, char* args){
|
||||
char *ifname = splitargs(args, NULL);
|
||||
if(ifname){ // setter
|
||||
int l = strlen(ifname);
|
||||
if(l > MAX_IINTERFACE_SZ) return ERR_BADPAR; // too long
|
||||
the_conf.iIlength = (uint8_t) l * 2;
|
||||
char *ptr = (char*)the_conf.iInterface;
|
||||
for(int i = 0; i < l; ++i){
|
||||
*ptr++ = *ifname++;
|
||||
*ptr++ = 0;
|
||||
}
|
||||
}
|
||||
// getter
|
||||
CMDEQ();
|
||||
int l = the_conf.iIlength / 2;
|
||||
char *ptr = (char*)the_conf.iInterface;
|
||||
for(int i = 0; i < l; ++i){
|
||||
SEND(ptr); // next is always zero
|
||||
ptr += 2;
|
||||
}
|
||||
SEND("\n");
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_restonstart(const char* cmd, char* args){
|
||||
int32_t val;
|
||||
if(argsvals(args, NULL, &val)){ // setter
|
||||
if(!val) the_conf.flags.restore = 0;
|
||||
else the_conf.flags.restore = 1;
|
||||
}
|
||||
CMDEQ();
|
||||
SEND(u2str(the_conf.flags.restore));
|
||||
SEND("\n");
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static void showpar(const char *par, uint8_t n, uint8_t v){
|
||||
char c[2];
|
||||
c[0] = '0' + n; c[1] = 0;
|
||||
SEND(par); SEND(c); SEND(EQ);
|
||||
SEND(u2str(v));
|
||||
}
|
||||
|
||||
static errcodes_t cmd_dumpconf(const char*, char*){
|
||||
SEND("userconf_sz="); SEND(u2str(the_conf.userconf_sz));
|
||||
SEND("\ncurr_idx="); SEND(u2str(currentconfidx));
|
||||
SEND("\ncapacity="); SEND(u2str(maxCnum-2));
|
||||
cmd_setiface("\nsetiface", NULL);
|
||||
cmd_restonstart("restonstart", NULL);
|
||||
for(int i = 0; i < SENSORS_AMOUNT; ++i){
|
||||
showpar("gain", i, the_conf.spars[i].AFE_GB);
|
||||
showpar("\nlco_fdiv", i, the_conf.spars[i].LCO_FDIV);
|
||||
showpar("\nmaskdist", i, the_conf.spars[i].MASK_DIST);
|
||||
showpar("\nminnumlig", i, the_conf.spars[i].MIN_NUM_LIG);
|
||||
showpar("\nnflev", i, the_conf.spars[i].NF_LEV);
|
||||
showpar("\nsrej", i, the_conf.spars[i].SREJ);
|
||||
showpar("\ntuncap", i, the_conf.spars[i].TUN_CAP);
|
||||
showpar("\nwdthres", i, the_conf.spars[i].WDTH);
|
||||
SEND("\n");
|
||||
}
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_mcutemp(const char *cmd, char*){
|
||||
CMDEQ();
|
||||
SEND(i2str(getMCUtemp())); SEND("\n");
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_vdd(const char *cmd, char*){
|
||||
CMDEQ();
|
||||
SEND(u2str(getVdd())); SEND("\n");
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_help(const char*, char*){
|
||||
SEND(REPOURL);
|
||||
for(size_t i = 0; i < sizeof(cmdInfo)/sizeof(cmdInfo[0]); i++){
|
||||
SEND(cmdInfo[i].name);
|
||||
SEND(" - ");
|
||||
SEND(cmdInfo[i].desc); SEND("\n");
|
||||
}
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t senscmd8(int32_t CHno, int (*cmd)(uint8_t), int32_t arg){
|
||||
if(CHno < 0 || CHno >= SENSORS_AMOUNT) return ERR_BADPAR;
|
||||
uint8_t par = static_cast<uint8_t>(arg);
|
||||
as3935_channel = static_cast<uint8_t>(CHno);
|
||||
int ans = cmd(par);
|
||||
if(ans) return ERR_OK;
|
||||
return ERR_CANTRUN;
|
||||
}
|
||||
|
||||
static errcodes_t getta(const char *cmd, int32_t CHno, int (*fn)(uint8_t*)){
|
||||
if(CHno < 0 || CHno >= SENSORS_AMOUNT) return ERR_BADPAR;
|
||||
uint8_t par;
|
||||
as3935_channel = static_cast<uint8_t>(CHno);
|
||||
int ans = fn(&par);
|
||||
if(!ans) return ERR_CANTRUN;
|
||||
CMDEQP(CHno); SEND(u2str(par)); SEND("\n");
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t senscmd(int32_t CHno, int (*cmd)()){
|
||||
if(CHno < 0 || CHno >= SENSORS_AMOUNT) return ERR_BADPAR;
|
||||
as3935_channel = static_cast<uint8_t>(CHno);
|
||||
int ans = cmd();
|
||||
if(ans) return ERR_OK;
|
||||
return ERR_CANTRUN;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_displco(const char *cmd, char *args){
|
||||
int32_t CHno, val;
|
||||
if(!argsvals(args, &CHno, &val)) return getta(cmd, CHno, as3935_get_displco);
|
||||
errcodes_t ret = senscmd8(CHno, as3935_displco, val);
|
||||
if(ret == ERR_OK) DISPLCO[CHno] = val;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define AS3935_FN8(nm) \
|
||||
static errcodes_t cmd_ ## nm(const char* cmd, char* args){ \
|
||||
int32_t CHno = -1, val; \
|
||||
if(!argsvals(args, &CHno, &val)) return getta(cmd, CHno, as3935_get_ ## nm); \
|
||||
return senscmd8(CHno, as3935_ ## nm, val);}
|
||||
|
||||
#define AS3935_FN(nm) \
|
||||
static errcodes_t cmd_ ## nm(const char*, char* args){ \
|
||||
int32_t CHno = -1; \
|
||||
splitargs(args, &CHno); \
|
||||
return senscmd(CHno, as3935_ ## nm);}
|
||||
|
||||
AS3935_FN8(tuncap)
|
||||
AS3935_FN8(gain)
|
||||
AS3935_FN8(wdthres)
|
||||
AS3935_FN8(nflev)
|
||||
AS3935_FN8(srej)
|
||||
AS3935_FN8(minnumlig)
|
||||
AS3935_FN8(maskdist)
|
||||
AS3935_FN8(lco_fdiv)
|
||||
AS3935_FN(clearstat)
|
||||
AS3935_FN(resetdef)
|
||||
|
||||
#define AS3935_GU8(nm) \
|
||||
static errcodes_t cmd_ ## nm(const char* cmd, char* args){ \
|
||||
int32_t CHno = -1; uint8_t par;\
|
||||
splitargs(args, &CHno); \
|
||||
if(CHno < 0 || CHno >= SENSORS_AMOUNT) return ERR_BADPAR; \
|
||||
as3935_channel = static_cast<uint8_t>(CHno); \
|
||||
int ans = as3935_ ## nm(&par); \
|
||||
if(!ans) return ERR_CANTRUN; \
|
||||
CMDEQP(CHno); SEND(u2str(par)); SEND("\n"); \
|
||||
return ERR_AMOUNT; }
|
||||
|
||||
AS3935_GU8(intcode)
|
||||
AS3935_GU8(distance)
|
||||
|
||||
static errcodes_t cmd_iscalib(const char* cmd, char* args){
|
||||
int32_t CHno = -1;
|
||||
splitargs(args, &CHno);
|
||||
return getta(cmd, CHno, as3935_get_calib);
|
||||
}
|
||||
|
||||
static errcodes_t cmd_wakeup(const char*, char* args){
|
||||
int32_t CHno = -1;
|
||||
splitargs(args, &CHno);
|
||||
if(CHno < 0 || CHno >= SENSORS_AMOUNT) return ERR_BADPAR;
|
||||
as3935_channel = static_cast<uint8_t>(CHno);
|
||||
if(!as3935_wakeup() || !as3935_calib_rco()) return ERR_CANTRUN;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_energy(const char* cmd, char* args){
|
||||
int32_t CHno = -1; uint32_t par;
|
||||
splitargs(args, &CHno);
|
||||
if(CHno < 0 || CHno >= SENSORS_AMOUNT) return ERR_BADPAR;
|
||||
as3935_channel = static_cast<uint8_t>(CHno);
|
||||
if(!as3935_energy(&par)) return ERR_CANTRUN;
|
||||
CMDEQP(CHno); SEND(u2str(par)); SEND("\n");
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief parse_hex_data - data parsing in case of `hex + text` input format
|
||||
* @param input - input string
|
||||
* @param output - output data
|
||||
* @param max_len - length of `output`
|
||||
* @return amount of parsed bytes or -1 in case of overflow or error
|
||||
*/
|
||||
static int parse_hex_data(char *input, uint8_t *output, int max_len){
|
||||
if(!input || !*input || !output || max_len < 1) return 0;
|
||||
char *p = input;
|
||||
int out_idx = 0;
|
||||
while(*p && out_idx < max_len){
|
||||
while(*p == ' ' || *p == ',') ++p; // omit spaces and commas as delimeters
|
||||
if(*p == '\0') break; // EOL
|
||||
if(*p == '"'){ // TEXT (start/end)
|
||||
++p;
|
||||
while(*p && *p != '"'){
|
||||
if(out_idx >= max_len) return -1;
|
||||
output[out_idx++] = *p++;
|
||||
}
|
||||
if(*p == '"'){
|
||||
++p; // go to next symbol after closing quotation mark
|
||||
}else return -1; // no closing
|
||||
}else{ // HEX number
|
||||
char *start = p;
|
||||
while(*p && *p != ' ' && *p != ',' && *p != '"') ++p;
|
||||
char saved = *p;
|
||||
*p = '\0'; // temporarily for `gethex`
|
||||
uint32_t val;
|
||||
const char *end = gethex(start, &val);
|
||||
if(end != p || val > 0xFF){ // not a hex number or have more than 2 symbols
|
||||
*p = saved;
|
||||
return -1;
|
||||
}
|
||||
*p = saved;
|
||||
output[out_idx++] = (uint8_t)val;
|
||||
}
|
||||
}
|
||||
return out_idx;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_SPI(const char* cmd, char *args){
|
||||
if(!args) return ERR_BADVAL;
|
||||
if(!(SPI1->CR1 & SPI_CR1_SPE)) return ERR_CANTRUN;
|
||||
int32_t CHno;
|
||||
char *setter = splitargs(args, &CHno);
|
||||
if(!setter) return ERR_BADVAL;
|
||||
if(CHno < 0 || CHno >= SENSORS_AMOUNT) return ERR_BADPAR;
|
||||
int len = parse_hex_data(setter, curbuf, MAXSTRLEN);
|
||||
if(len <= 0) return ERR_BADVAL;
|
||||
as3935_channel = static_cast<uint8_t>(CHno);
|
||||
uint8_t ret = SPI_transmit(curbuf, len);
|
||||
if(!ret) return ERR_BUSY;
|
||||
CMDEQP(CHno);
|
||||
if(len > 8) SEND("\n");
|
||||
hexdump(SEND, curbuf, len);
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
constexpr uint32_t hash(const char* str, uint32_t h = 0){
|
||||
return *str ? hash(str + 1, h + ((h << 7) ^ *str)) : h;
|
||||
}
|
||||
|
||||
const char *parse_cmd(int (*sendfun)(const char *), char *str){
|
||||
SEND = sendfun;
|
||||
char command[CMD_MAXLEN+1];
|
||||
int i = 0;
|
||||
while(*str > '@' && i < CMD_MAXLEN){ command[i++] = *str++; }
|
||||
command[i] = 0;
|
||||
while(*str && *str <= ' ') ++str;
|
||||
char *restof = (char*) str;
|
||||
uint32_t h = hash(command);
|
||||
errcodes_t ecode = ERR_AMOUNT;
|
||||
switch(h){
|
||||
#define COMMAND(name, desc) case hash(#name): ecode = cmd_ ## name(command, restof); break;
|
||||
COMMAND_TABLE
|
||||
#undef COMMAND
|
||||
default: SEND("Unknown command, try 'help'\n"); break;
|
||||
}
|
||||
if(ecode < ERR_AMOUNT) return errtxt[ecode];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// show lightning information
|
||||
void lightning_info(int (*sendfun)(const char*), uint8_t CHno){
|
||||
if(!sendfun || CHno >= SENSORS_AMOUNT) return;
|
||||
char c[2];
|
||||
c[0] = '0' + CHno; c[1] = 0;
|
||||
SEND = sendfun;
|
||||
cmd_energy("energy", c);
|
||||
cmd_distance("distance", c);
|
||||
}
|
||||
54
F1:F103/AS3935-lightning/commproto.h
Normal file
54
F1:F103/AS3935-lightning/commproto.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of the as3935 project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "version.inc"
|
||||
|
||||
#ifdef EBUG
|
||||
#define RLSDBG "debug"
|
||||
#else
|
||||
#define RLSDBG "release"
|
||||
#endif
|
||||
|
||||
#define REPOURL "https://github.com/eddyem/stm32samples/tree/master/F1:F103/AS3935-lightning " RLSDBG " build #" BUILD_NUMBER "@" BUILD_DATE "\n"
|
||||
|
||||
|
||||
// error codes for answer message
|
||||
typedef enum{
|
||||
ERR_OK, // all OK
|
||||
ERR_BADCMD, // wrong command
|
||||
ERR_BADPAR, // wrong parameter
|
||||
ERR_BADVAL, // wrong value (for setter)
|
||||
ERR_WRONGLEN, // wrong message length
|
||||
ERR_CANTRUN, // can't run given command due to bad parameters or other
|
||||
ERR_BUSY, // target interface busy, try later
|
||||
ERR_OVERFLOW, // string was too long -> overflow
|
||||
ERR_AMOUNT // amount of error codes or "send nothing"
|
||||
} errcodes_t;
|
||||
|
||||
// maximal length of command (without trailing zero)
|
||||
#define CMD_MAXLEN 15
|
||||
// maximal available parameter number
|
||||
#define MAXPARNO 255
|
||||
// maximal string length includint terminating zero
|
||||
#define MAXSTRLEN 256
|
||||
|
||||
extern const char *EQ;
|
||||
const char *parse_cmd(int (*sendfun)(const char*), char *buf);
|
||||
void lightning_info(int (*sendfun)(const char*), uint8_t CHno);
|
||||
169
F1:F103/AS3935-lightning/flash.c
Normal file
169
F1:F103/AS3935-lightning/flash.c
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* This file is part of the as3935 project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stm32f1.h>
|
||||
#include <string.h> // memcpy
|
||||
|
||||
#include "flash.h"
|
||||
#include "strfunc.h"
|
||||
|
||||
extern const uint32_t _BLOCKSIZE;
|
||||
extern const user_conf __varsstart;
|
||||
|
||||
static const uint32_t FLASH_blocksize = (uint32_t)&_BLOCKSIZE;
|
||||
uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here
|
||||
|
||||
static int write2flash(const void*, const void*, uint32_t);
|
||||
const user_conf *Flash_Data = (const user_conf *)(&__varsstart);
|
||||
user_conf the_conf = {
|
||||
.userconf_sz = sizeof(user_conf),
|
||||
};
|
||||
|
||||
int currentconfidx = -1; // index of current configuration
|
||||
|
||||
/**
|
||||
* @brief binarySearch - binary search in flash for last non-empty cell
|
||||
* any struct searched should have its sizeof() @ the first field!!!
|
||||
* @param l - left index
|
||||
* @param r - right index (should be @1 less than last index!)
|
||||
* @param start - starting address
|
||||
* @param stor_size - size of structure to search
|
||||
* @return index of non-empty cell or -1
|
||||
*/
|
||||
static int binarySearch(int r, const uint8_t *start, int stor_size){
|
||||
int l = 0;
|
||||
while(r >= l){
|
||||
int mid = l + (r - l) / 2;
|
||||
const uint8_t *s = start + mid * stor_size;
|
||||
if(*((const uint16_t*)s) == stor_size){
|
||||
if(*((const uint16_t*)(s + stor_size)) == 0xffff){ // next is free
|
||||
return mid;
|
||||
}else{ // element is to the right
|
||||
l = mid + 1;
|
||||
}
|
||||
}else{ // element is to the left
|
||||
r = mid - 1;
|
||||
}
|
||||
}
|
||||
return -1; // not found
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief flashstorage_init - initialization of user conf storage
|
||||
* run in once @ start
|
||||
*/
|
||||
void flashstorage_init(){
|
||||
if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){
|
||||
uint32_t flsz = FLASH_SIZE * 1024; // size in bytes
|
||||
flsz -= (uint32_t)(&__varsstart) - FLASH_BASE;
|
||||
maxCnum = flsz / sizeof(user_conf);
|
||||
}
|
||||
// -1 if there's no data at all & flash is clear; maxnum-1 if flash is full
|
||||
currentconfidx = binarySearch((int)maxCnum-2, (const uint8_t*)Flash_Data, sizeof(user_conf));
|
||||
if(currentconfidx > -1){
|
||||
memcpy(&the_conf, &Flash_Data[currentconfidx], sizeof(user_conf));
|
||||
}
|
||||
}
|
||||
|
||||
// store new configuration
|
||||
// @return 0 if all OK
|
||||
int store_userconf(){
|
||||
// maxnum - 3 means that there always should be at least one empty record after last data
|
||||
// for binarySearch() checking that there's nothing more after it!
|
||||
if(currentconfidx > (int)maxCnum - 3){ // there's no more place
|
||||
currentconfidx = 0;
|
||||
if(erase_storage(-1)) return 1;
|
||||
}else ++currentconfidx; // take next data position (0 - within first run after firmware flashing)
|
||||
return write2flash((const void*)&Flash_Data[currentconfidx], &the_conf, sizeof(the_conf));
|
||||
}
|
||||
|
||||
static int write2flash(const void *start, const void *wrdata, uint32_t stor_size){
|
||||
int ret = 0;
|
||||
if (FLASH->CR & FLASH_CR_LOCK){ // unloch flash
|
||||
FLASH->KEYR = FLASH_KEY1;
|
||||
FLASH->KEYR = FLASH_KEY2;
|
||||
}
|
||||
while (FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH;
|
||||
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR; // clear all flags
|
||||
FLASH->CR |= FLASH_CR_PG;
|
||||
const uint16_t *data = (const uint16_t*) wrdata;
|
||||
volatile uint16_t *address = (volatile uint16_t*) start;
|
||||
uint32_t i, count = (stor_size + 1) / 2;
|
||||
for(i = 0; i < count; ++i){
|
||||
*(volatile uint16_t*)(address + i) = data[i];
|
||||
while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH;
|
||||
if(*(volatile uint16_t*)(address + i) != data[i]){
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
if(FLASH->SR & FLASH_SR_PGERR){
|
||||
ret = 1; // program error - meet not 0xffff
|
||||
break;
|
||||
}
|
||||
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR;
|
||||
}
|
||||
FLASH->CR &= ~(FLASH_CR_PG);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// erase Nth page of flash storage (flash should be prepared!)
|
||||
static int erase_pageN(int N){
|
||||
int ret = 0;
|
||||
FLASH->AR = (uint32_t)Flash_Data + N*FLASH_blocksize;
|
||||
FLASH->CR |= FLASH_CR_STRT;
|
||||
while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH;
|
||||
FLASH->SR = FLASH_SR_EOP;
|
||||
if(FLASH->SR & FLASH_SR_WRPRTERR){ /* Check Write protection error */
|
||||
ret = 1;
|
||||
FLASH->SR = FLASH_SR_WRPRTERR; /* Clear the flag by software by writing it at 1*/
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// erase full storage (npage < 0) or its nth page; @return 0 if all OK
|
||||
int erase_storage(int npage){
|
||||
int ret = 0;
|
||||
uint32_t end = 1, start = 0, flsz = 0;
|
||||
if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){
|
||||
flsz = FLASH_SIZE * 1024; // size in bytes
|
||||
flsz -= (uint32_t)Flash_Data - FLASH_BASE;
|
||||
}
|
||||
end = flsz / FLASH_blocksize;
|
||||
if(end == 0 || end >= FLASH_SIZE) return 1;
|
||||
if(npage > -1){ // erase only one page
|
||||
if((uint32_t)npage >= end) return 1;
|
||||
start = npage;
|
||||
end = start + 1;
|
||||
}
|
||||
if((FLASH->CR & FLASH_CR_LOCK) != 0){
|
||||
FLASH->KEYR = FLASH_KEY1;
|
||||
FLASH->KEYR = FLASH_KEY2;
|
||||
}
|
||||
while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH;
|
||||
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR;
|
||||
FLASH->CR |= FLASH_CR_PER;
|
||||
for(uint32_t i = start; i < end; ++i){
|
||||
if(erase_pageN(i)){
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
FLASH->CR &= ~FLASH_CR_PER;
|
||||
return ret;
|
||||
}
|
||||
|
||||
67
F1:F103/AS3935-lightning/flash.h
Normal file
67
F1:F103/AS3935-lightning/flash.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This file is part of the as3935 project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "hardware.h" // for SENSORS_AMOUNT
|
||||
|
||||
#define FLASH_SIZE_REG ((uint32_t)0x1FFFF7E0)
|
||||
#define FLASH_SIZE *((uint16_t*)FLASH_SIZE_REG)
|
||||
|
||||
// maximal size (in letters) of iInterface for settings
|
||||
#define MAX_IINTERFACE_SZ (16)
|
||||
|
||||
typedef struct{
|
||||
// t_afe_gain
|
||||
uint8_t AFE_GB : 5;
|
||||
// t_threshold
|
||||
uint8_t WDTH : 4;
|
||||
uint8_t NF_LEV : 3;
|
||||
// t_lightning_reg
|
||||
uint8_t SREJ : 4;
|
||||
uint8_t MIN_NUM_LIG : 2;
|
||||
// t_int_mask_ant
|
||||
uint8_t MASK_DIST : 1;
|
||||
uint8_t LCO_FDIV : 2;
|
||||
// t_tun_disp
|
||||
uint8_t TUN_CAP : 4;
|
||||
} sens_pars_t;
|
||||
|
||||
typedef struct{
|
||||
uint8_t restore : 1; // restore sensors' parameters on start
|
||||
} flags_t;
|
||||
|
||||
/*
|
||||
* struct to save user configurations
|
||||
*/
|
||||
typedef struct __attribute__((packed, aligned(4))){
|
||||
uint16_t userconf_sz; // "magick number"
|
||||
uint16_t iInterface[MAX_IINTERFACE_SZ]; // interface name
|
||||
uint8_t iIlength; // length in symbols
|
||||
sens_pars_t spars[SENSORS_AMOUNT]; // sensors' stored flags
|
||||
flags_t flags; // common flags
|
||||
} user_conf;
|
||||
|
||||
extern uint32_t maxCnum;
|
||||
extern user_conf the_conf;
|
||||
extern int currentconfidx;
|
||||
|
||||
void flashstorage_init();
|
||||
int store_userconf();
|
||||
int erase_storage(int npage);
|
||||
70
F1:F103/AS3935-lightning/hardware.c
Normal file
70
F1:F103/AS3935-lightning/hardware.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of the as3935 project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "adc.h"
|
||||
#include "hardware.h"
|
||||
#include "spi.h"
|
||||
|
||||
/*
|
||||
* PA0 - INT0 - PD in
|
||||
* PA1 - INT1 - PD in
|
||||
* PA2 - CS0 - PP out
|
||||
* PA3 - CS1 - PP out
|
||||
* PA5 - SPI1 SCK
|
||||
* PA6 - SPI1 MISO
|
||||
* PA7 - SPI1 MOSI
|
||||
* PA9 - USART1 Tx
|
||||
* PA10- USART1 Rx
|
||||
*/
|
||||
|
||||
static inline void gpio_setup(){
|
||||
// Enable clocks to the GPIO subsystems (PB for ADC), turn on AFIO clocking to disable SWD/JTAG
|
||||
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN | RCC_APB2ENR_SPI1EN;
|
||||
// turn off SWJ/JTAG
|
||||
AFIO->MAPR = AFIO_MAPR_SWJ_CFG_JTAGDISABLE; // for PA15
|
||||
// Set led as opendrain output
|
||||
GPIOC->CRH |= CRH(13, CNF_ODOUTPUT|MODE_SLOW);
|
||||
// PA pins
|
||||
GPIOA->ODR = 0; // for pull-down
|
||||
GPIOA->CRL = CRL(0, CNF_PUDINPUT|MODE_INPUT) | CRL(1, CNF_PUDINPUT|MODE_INPUT) | CRL(2, CNF_PPOUTPUT|MODE_SLOW) |
|
||||
CRL(3, CNF_PPOUTPUT|MODE_SLOW) | CRL(5, CNF_AFPP|MODE_FAST) | CRL(6, CNF_FLINPUT|MODE_INPUT) | CRL(7, CNF_AFPP|MODE_FAST);
|
||||
GPIOA->CRH = CRH(9, CNF_AFPP|MODE_FAST) | CRH(10, CNF_FLINPUT|MODE_INPUT) | CRH(15, CNF_PPOUTPUT|MODE_SLOW);
|
||||
CS_OFF();
|
||||
}
|
||||
|
||||
void hw_setup(){
|
||||
gpio_setup();
|
||||
adc_setup();
|
||||
spi_setup();
|
||||
}
|
||||
|
||||
#ifndef EBUG
|
||||
void iwdg_setup(){
|
||||
uint32_t tmout = 16000000;
|
||||
RCC->CSR |= RCC_CSR_LSION;
|
||||
while((RCC->CSR & RCC_CSR_LSIRDY) != RCC_CSR_LSIRDY){if(--tmout == 0) break;} /* (2) */
|
||||
IWDG->KR = IWDG_START;
|
||||
IWDG->KR = IWDG_WRITE_ACCESS;
|
||||
IWDG->PR = IWDG_PR_PR_1;
|
||||
IWDG->RLR = 1250;
|
||||
tmout = 16000000;
|
||||
while(IWDG->SR){if(--tmout == 0) break;}
|
||||
IWDG->KR = IWDG_REFRESH;
|
||||
}
|
||||
#endif
|
||||
|
||||
57
F1:F103/AS3935-lightning/hardware.h
Normal file
57
F1:F103/AS3935-lightning/hardware.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This file is part of the as3935 project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stm32f1.h>
|
||||
|
||||
// amount of connected sensors
|
||||
#define SENSORS_AMOUNT 2
|
||||
|
||||
// LED0 - PC13 (bluepill), blinking each second
|
||||
#define LED0_port GPIOC
|
||||
#define LED0_pin (1<<13)
|
||||
|
||||
// USB pullup (not present in bluepill, should be soldered) - PA15
|
||||
#define USBPU_port GPIOA
|
||||
#define USBPU_pin (1<<15)
|
||||
#define USBPU_ON() pin_set(USBPU_port, USBPU_pin)
|
||||
#define USBPU_OFF() pin_clear(USBPU_port, USBPU_pin)
|
||||
|
||||
#define LED_blink(x) pin_toggle(x ## _port, x ## _pin)
|
||||
#define LED_on(x) pin_clear(x ## _port, x ## _pin)
|
||||
#define LED_off(x) pin_set(x ## _port, x ## _pin)
|
||||
|
||||
// CS pins: PA2/PA3
|
||||
#define CS_OFF() do{GPIOA->BSRR = 0xc;}while(0)
|
||||
#define CS(x) do{GPIOA->BSRR = (x) ? (1<<19 | 1<<2) : (1<<18 | 1<<3);}while(0)
|
||||
|
||||
// INT pins: PA0/PA1
|
||||
#define CHK_INT(x) ((GPIOA->IDR & (1<<x)) ? 1 : 0)
|
||||
|
||||
enum{
|
||||
DISPLCO_NOTHING,
|
||||
DISPLCO_TRCO,
|
||||
DISPLCO_SRCO,
|
||||
DISPLCO_LCO
|
||||
};
|
||||
|
||||
extern uint8_t DISPLCO[SENSORS_AMOUNT];
|
||||
|
||||
void hw_setup();
|
||||
void iwdg_setup();
|
||||
109
F1:F103/AS3935-lightning/main.c
Normal file
109
F1:F103/AS3935-lightning/main.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* This file is part of the as3935 project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "as3935.h"
|
||||
#include "flash.h"
|
||||
#include "hardware.h"
|
||||
#include "commproto.h"
|
||||
#include "flash.h"
|
||||
#include "spi.h"
|
||||
#include "strfunc.h"
|
||||
#include "usb_dev.h"
|
||||
|
||||
uint8_t DISPLCO[SENSORS_AMOUNT] = {0};
|
||||
|
||||
volatile uint32_t Tms = 0;
|
||||
static char inbuff[RBINSZ];
|
||||
|
||||
/* Called when systick fires */
|
||||
void sys_tick_handler(void){
|
||||
++Tms;
|
||||
}
|
||||
|
||||
int main(){
|
||||
uint8_t oldint[2] = {0}; // ==1 for interrupted pins
|
||||
uint32_t lastT = 0;
|
||||
StartHSE();
|
||||
//flashstorage_init();
|
||||
hw_setup();
|
||||
USBPU_OFF();
|
||||
SysTick_Config(72000);
|
||||
flashstorage_init();
|
||||
USB_setup();
|
||||
#ifndef EBUG
|
||||
iwdg_setup();
|
||||
#endif
|
||||
//usart_setup();
|
||||
USBPU_ON();
|
||||
// wake-up all sensors and run auto-calibration
|
||||
for(uint8_t ch = 0; ch < SENSORS_AMOUNT; ++ch){
|
||||
as3935_channel = ch;
|
||||
as3935_wakeup();
|
||||
as3935_calib_rco();
|
||||
if(the_conf.flags.restore){
|
||||
sens_pars_t *p = &the_conf.spars[ch];
|
||||
as3935_gain(p->AFE_GB);
|
||||
as3935_wdthres(p->WDTH);
|
||||
as3935_nflev(p->NF_LEV);
|
||||
as3935_srej(p->SREJ);
|
||||
as3935_minnumlig(p->MIN_NUM_LIG);
|
||||
as3935_maskdist(p->MASK_DIST);
|
||||
as3935_lco_fdiv(p->LCO_FDIV);
|
||||
as3935_tuncap(p->TUN_CAP);
|
||||
}
|
||||
}
|
||||
while(1){
|
||||
IWDG->KR = IWDG_REFRESH; // refresh watchdog
|
||||
if(Tms - lastT > 499){
|
||||
LED_blink(LED0);
|
||||
lastT = Tms;
|
||||
}
|
||||
int l = USB_receivestr(inbuff, RBINSZ);
|
||||
if(l < 0) USB_sendstr("ERROR: CMD USB buffer overflow or string was too long");
|
||||
else if(l){
|
||||
const char *ans = parse_cmd(USB_sendstr, inbuff);
|
||||
if(ans) USB_sendstr(ans);
|
||||
}
|
||||
for(uint8_t ch = 0; ch < SENSORS_AMOUNT; ++ch){
|
||||
if(DISPLCO[ch] != DISPLCO_NOTHING) continue; // don't check IRQ in if it used as clock output
|
||||
if(CHK_INT(ch)){
|
||||
if(oldint[ch] == 0){
|
||||
oldint[ch] = 1;
|
||||
uint8_t code;
|
||||
as3935_channel = ch;
|
||||
int result = as3935_intcode(&code);
|
||||
if(!result) USB_sendstr("CANTGET\n");
|
||||
else if(code){
|
||||
uint8_t savedcode = code;
|
||||
USB_sendstr("INTERRUPT"); USB_putbyte('0'+ch); USB_putbyte('=');
|
||||
const char *delim = NULL, *comma = ",";
|
||||
if(code & INT_NH){ USB_sendstr("NOICE"); delim = comma; code &= ~INT_NH; }
|
||||
if(code & INT_D){ USB_sendstr(delim); USB_sendstr("DISTURBER"); delim = comma; code &= ~INT_D; }
|
||||
if(code & INT_L){ USB_sendstr(delim); USB_sendstr("LIGHTNING"); code &= ~INT_L; }
|
||||
if(code) USB_sendstr(u2str(code));
|
||||
newline();
|
||||
if(savedcode == INT_L){ // clear lightning - show distance and power
|
||||
lightning_info(USB_sendstr, ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else oldint[ch] = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
4
F1:F103/AS3935-lightning/openocd.cfg
Normal file
4
F1:F103/AS3935-lightning/openocd.cfg
Normal file
@@ -0,0 +1,4 @@
|
||||
set FLASH_SIZE 0x10000
|
||||
|
||||
source [find interface/stlink.cfg]
|
||||
source [find target/stm32f1x.cfg]
|
||||
203
F1:F103/AS3935-lightning/ringbuffer.c
Normal file
203
F1:F103/AS3935-lightning/ringbuffer.c
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#define CHK(b) do{if(!b) return -1;}while(0)
|
||||
|
||||
static int datalen(ringbuffer *b){
|
||||
if(b->tail >= b->head) return (b->tail - b->head);
|
||||
else return (b->length - b->head + b->tail);
|
||||
}
|
||||
|
||||
// stored data length
|
||||
int RB_datalen(ringbuffer *b){
|
||||
CHK(b);
|
||||
if(0 == datalen(b)) return 0; // don't block for empty RO operations
|
||||
if(b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int l = datalen(b);
|
||||
b->busy = 0;
|
||||
return l;
|
||||
}
|
||||
|
||||
static int hasbyte(ringbuffer *b, uint8_t byte){
|
||||
if(b->head == b->tail) return -1; // no data in buffer
|
||||
int startidx = b->head;
|
||||
if(b->head > b->tail){ //
|
||||
for(int found = b->head; found < b->length; ++found)
|
||||
if(b->data[found] == byte) return found;
|
||||
startidx = 0;
|
||||
}
|
||||
for(int found = startidx; found < b->tail; ++found)
|
||||
if(b->data[found] == byte) return found;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief RB_hasbyte - check if buffer has given byte stored
|
||||
* @param b - buffer
|
||||
* @param byte - byte to find
|
||||
* @return index if found, -1 if none or busy
|
||||
*/
|
||||
int RB_hasbyte(ringbuffer *b, uint8_t byte){
|
||||
CHK(b);
|
||||
if(b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int ret = hasbyte(b, byte);
|
||||
b->busy = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// increment head or tail
|
||||
TRUE_INLINE void incr(ringbuffer *b, volatile int *what, int n){
|
||||
*what += n;
|
||||
if(*what >= b->length) *what -= b->length;
|
||||
}
|
||||
|
||||
static int read(ringbuffer *b, uint8_t *s, int len){
|
||||
int l = datalen(b);
|
||||
if(!l) return 0;
|
||||
if(l > len) l = len;
|
||||
int _1st = b->length - b->head;
|
||||
if(_1st > l) _1st = l;
|
||||
if(_1st > len) _1st = len;
|
||||
memcpy(s, b->data + b->head, _1st);
|
||||
if(_1st < len && l > _1st){
|
||||
memcpy(s+_1st, b->data, l - _1st);
|
||||
incr(b, &b->head, l);
|
||||
return l;
|
||||
}
|
||||
incr(b, &b->head, _1st);
|
||||
return _1st;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief RB_read - read data from ringbuffer
|
||||
* @param b - buffer
|
||||
* @param s - array to write data
|
||||
* @param len - max len of `s`
|
||||
* @return bytes read or -1 if busy
|
||||
*/
|
||||
int RB_read(ringbuffer *b, uint8_t *s, int len){
|
||||
CHK(b);
|
||||
if(!s || len < 1) return -1;
|
||||
if(0 == datalen(b)) return 0;
|
||||
if(b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int r = read(b, s, len);
|
||||
b->busy = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
// length of data from current position to `byte` (including byte)
|
||||
static int lento(ringbuffer *b, uint8_t byte){
|
||||
int idx = hasbyte(b, byte);
|
||||
if(idx < 0) return 0;
|
||||
int partlen = idx + 1 - b->head;
|
||||
// now calculate length of new data portion
|
||||
if(idx < b->head) partlen += b->length;
|
||||
return partlen;
|
||||
}
|
||||
|
||||
static int readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
|
||||
int partlen = lento(b, byte);
|
||||
if(!partlen) return 0;
|
||||
if(partlen > len) return -1;
|
||||
return read(b, s, partlen);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief RB_readto fill array `s` with data until byte `byte` (with it)
|
||||
* @param b - ringbuffer
|
||||
* @param byte - check byte
|
||||
* @param s - buffer to write data or NULL to clear data
|
||||
* @param len - length of `s` or 0 to clear data
|
||||
* @return amount of bytes written (negative, if len<data in buffer or buffer is busy)
|
||||
*/
|
||||
int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
|
||||
CHK(b);
|
||||
if(!s || len < 1) return -1;
|
||||
if(0 == datalen(b)) return 0;
|
||||
if(b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int n = 0;
|
||||
if(s && len > 0){
|
||||
n = readto(b, byte, s, len);
|
||||
}else{
|
||||
incr(b, &b->head, lento(b, byte)); // just throw data out
|
||||
}
|
||||
b->busy = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
int RB_datalento(ringbuffer *b, uint8_t byte){
|
||||
CHK(b);
|
||||
if(0 == datalen(b)) return 0;
|
||||
if(b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int n = lento(b, byte);
|
||||
b->busy = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
// if l < rest of buffer, truncate and return actually written bytes
|
||||
static int write(ringbuffer *b, const uint8_t *str, int l){
|
||||
int r = b->length - 1 - datalen(b); // rest length
|
||||
if(r < 1) return 0;
|
||||
if(l > r) l = r;
|
||||
int _1st = b->length - b->tail;
|
||||
if(_1st > l) _1st = l;
|
||||
memcpy(b->data + b->tail, str, _1st);
|
||||
if(_1st < l){ // add another piece from start
|
||||
memcpy(b->data, str+_1st, l-_1st);
|
||||
}
|
||||
incr(b, &b->tail, l);
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief RB_write - write some data to ringbuffer
|
||||
* @param b - buffer
|
||||
* @param str - data
|
||||
* @param l - length
|
||||
* @return amount of bytes written or -1 if busy
|
||||
*/
|
||||
int RB_write(ringbuffer *b, const uint8_t *str, int l){
|
||||
CHK(b);
|
||||
if(!str || l < 1) return -1;
|
||||
if(b->length - datalen(b) < 2) return 0;
|
||||
if(b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int w = write(b, str, l);
|
||||
b->busy = 0;
|
||||
return w;
|
||||
}
|
||||
|
||||
// just delete all information in buffer `b`
|
||||
int RB_clearbuf(ringbuffer *b){
|
||||
CHK(b);
|
||||
if(b->busy) return -1;
|
||||
b->busy = 1;
|
||||
b->head = 0;
|
||||
b->tail = 0;
|
||||
bzero(b->data, b->length);
|
||||
b->busy = 0;
|
||||
return 1;
|
||||
}
|
||||
44
F1:F103/AS3935-lightning/ringbuffer.h
Normal file
44
F1:F103/AS3935-lightning/ringbuffer.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined STM32F0
|
||||
#include <stm32f0.h>
|
||||
#elif defined STM32F1
|
||||
#include <stm32f1.h>
|
||||
#elif defined STM32F3
|
||||
#include <stm32f3.h>
|
||||
#endif
|
||||
|
||||
typedef struct{
|
||||
uint8_t *data; // data buffer
|
||||
const int length; // its length
|
||||
int head; // head index
|
||||
int tail; // tail index
|
||||
volatile int busy; // == TRUE if buffer is busy now
|
||||
} ringbuffer;
|
||||
|
||||
int RB_read(ringbuffer *b, uint8_t *s, int len);
|
||||
int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len);
|
||||
int RB_hasbyte(ringbuffer *b, uint8_t byte);
|
||||
int RB_write(ringbuffer *b, const uint8_t *str, int l);
|
||||
int RB_datalen(ringbuffer *b);
|
||||
int RB_datalento(ringbuffer *b, uint8_t byte);
|
||||
int RB_clearbuf(ringbuffer *b);
|
||||
61
F1:F103/AS3935-lightning/spi.c
Normal file
61
F1:F103/AS3935-lightning/spi.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of the as3935 project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "spi.h"
|
||||
#include "hardware.h"
|
||||
#include "commproto.h"
|
||||
|
||||
// CR1 register default values
|
||||
// Fpclk/64
|
||||
static const uint32_t SPI_CR1 = SPI_CR1_MSTR | SPI_CR1_BR_2 | SPI_CR1_BR_0 | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_CPHA /* | SPI_CR1_CPOL*/;
|
||||
|
||||
spiStatus SPI_status = SPI_NOTREADY;
|
||||
uint8_t as3935_channel = 0;
|
||||
|
||||
void spi_setup(){
|
||||
// master, no slave select, BR=F/16, CPOL/CPHA - polarity.
|
||||
SPI1->CR1 = SPI_CR1;
|
||||
SPI_status = SPI_READY;
|
||||
SPI1->CR1 |= SPI_CR1_SPE; // enable SPI
|
||||
}
|
||||
|
||||
volatile uint32_t wctr;
|
||||
#define WAITX(x) do{wctr = 0; while((x) && (++wctr < 360000)) IWDG->KR = IWDG_REFRESH; if(wctr==360000) return -1;}while(0)
|
||||
|
||||
/**
|
||||
* @brief SPI_transmit - transmit data and receive new one
|
||||
* @param buf - data to transmit/receive
|
||||
* @param len - its length
|
||||
* @return amount of transmitted data or -1 if error
|
||||
*/
|
||||
uint8_t SPI_transmit(uint8_t *buf, uint8_t len){
|
||||
if(!buf || !len) return 0; // bad data format
|
||||
if(SPI_status != SPI_READY) return 0; // spi not ready to transmit data
|
||||
CS(as3935_channel);
|
||||
for(uint8_t x = 0; x < len; ++x){
|
||||
WAITX(!(SPI1->SR & SPI_SR_TXE));
|
||||
SPI1->DR = buf[x];
|
||||
WAITX(!(SPI1->SR & SPI_SR_BSY));
|
||||
WAITX(!(SPI1->SR & SPI_SR_RXNE));
|
||||
buf[x] = SPI1->DR;
|
||||
}
|
||||
CS_OFF();
|
||||
return len;
|
||||
}
|
||||
|
||||
35
F1:F103/AS3935-lightning/spi.h
Normal file
35
F1:F103/AS3935-lightning/spi.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of the as3935 project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stm32f1.h>
|
||||
|
||||
typedef enum{
|
||||
SPI_NOTREADY,
|
||||
SPI_READY,
|
||||
SPI_BUSY
|
||||
} spiStatus;
|
||||
|
||||
extern spiStatus SPI_status;
|
||||
// SPI channel number for `CS` macro
|
||||
extern uint8_t as3935_channel;
|
||||
|
||||
void spi_setup();
|
||||
uint8_t SPI_transmit(uint8_t *buf, uint8_t len);
|
||||
|
||||
266
F1:F103/AS3935-lightning/strfunc.c
Normal file
266
F1:F103/AS3935-lightning/strfunc.c
Normal file
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* This file is part of the encoders project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stm32f1.h>
|
||||
#include <string.h>
|
||||
#include "usb_dev.h"
|
||||
|
||||
/**
|
||||
* @brief hexdump - dump hex array by 16 bytes in string
|
||||
* @param arr - array to dump
|
||||
* @param len - length of `arr`
|
||||
*/
|
||||
void hexdump(int (*sendfun)(const char *), uint8_t *arr, uint16_t len){
|
||||
char buf[52], *bptr = buf;
|
||||
for(uint16_t l = 0; l < len; ++l, ++arr){
|
||||
for(int16_t j = 1; j > -1; --j){
|
||||
register uint8_t half = (*arr >> (4*j)) & 0x0f;
|
||||
if(half < 10) *bptr++ = half + '0';
|
||||
else *bptr++ = half - 10 + 'a';
|
||||
}
|
||||
if(l % 16 == 15){
|
||||
*bptr++ = '\n';
|
||||
*bptr = 0;
|
||||
sendfun(buf);
|
||||
bptr = buf;
|
||||
}else *bptr++ = ' ';
|
||||
}
|
||||
if(bptr != buf){
|
||||
*bptr++ = '\n';
|
||||
*bptr = 0;
|
||||
sendfun(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief _2str - convert value into string buffer
|
||||
* @param val - |value|
|
||||
* @param minus - ==0 if value > 0
|
||||
* @return buffer with number
|
||||
*/
|
||||
static char *_2str(uint32_t val, uint8_t minus){
|
||||
static char strbuf[12];
|
||||
char *bufptr = &strbuf[11];
|
||||
*bufptr = 0;
|
||||
if(!val){
|
||||
*(--bufptr) = '0';
|
||||
}else{
|
||||
while(val){
|
||||
uint32_t x = val / 10;
|
||||
*(--bufptr) = (val - 10*x) + '0';
|
||||
val = x;
|
||||
}
|
||||
}
|
||||
if(minus) *(--bufptr) = '-';
|
||||
return bufptr;
|
||||
}
|
||||
|
||||
// return string with number `val`
|
||||
char *u2str(uint32_t val){
|
||||
return _2str(val, 0);
|
||||
}
|
||||
char *i2str(int32_t i){
|
||||
uint8_t minus = 0;
|
||||
uint32_t val;
|
||||
if(i < 0){
|
||||
minus = 1;
|
||||
val = -i;
|
||||
}else val = i;
|
||||
return _2str(val, minus);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief uhex2str - print 32bit unsigned int as hex
|
||||
* @param val - value
|
||||
* @return string with number
|
||||
*/
|
||||
char *uhex2str(uint32_t val){
|
||||
static char buf[12] = "0x";
|
||||
int npos = 2;
|
||||
uint8_t *ptr = (uint8_t*)&val + 3;
|
||||
int8_t i, j, z=1;
|
||||
for(i = 0; i < 4; ++i, --ptr){
|
||||
if(*ptr == 0){ // omit leading zeros
|
||||
if(i == 3) z = 0;
|
||||
if(z) continue;
|
||||
}
|
||||
else z = 0;
|
||||
for(j = 1; j > -1; --j){
|
||||
uint8_t half = (*ptr >> (4*j)) & 0x0f;
|
||||
if(half < 10) buf[npos++] = half + '0';
|
||||
else buf[npos++] = half - 10 + 'a';
|
||||
}
|
||||
}
|
||||
buf[npos] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief omit_spaces - eliminate leading spaces and other trash in string
|
||||
* @param buf - string
|
||||
* @return - pointer to first character in `buf` > ' '
|
||||
*/
|
||||
char *omit_spaces(char *buf){
|
||||
while(*buf){
|
||||
if(*buf > ' ') break;
|
||||
++buf;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief getdec - read decimal number & return pointer to next non-number symbol
|
||||
* @param buf - string
|
||||
* @param N - number read
|
||||
* @return Next non-number symbol. In case of overflow return `buf` and N==0xffffffff
|
||||
*/
|
||||
static char *getdec(char *buf, uint32_t *N){
|
||||
char *start = (char*)buf;
|
||||
uint32_t num = 0;
|
||||
while(*buf){
|
||||
char c = *buf;
|
||||
if(c < '0' || c > '9'){
|
||||
break;
|
||||
}
|
||||
if(num > 429496729 || (num == 429496729 && c > '5')){ // overflow
|
||||
*N = 0xffffff;
|
||||
return start;
|
||||
}
|
||||
num *= 10;
|
||||
num += c - '0';
|
||||
++buf;
|
||||
}
|
||||
*N = num;
|
||||
return buf;
|
||||
}
|
||||
// read hexadecimal number (without 0x prefix!)
|
||||
char *gethex(char *buf, uint32_t *N){
|
||||
char *start = buf;
|
||||
uint32_t num = 0;
|
||||
while(*buf){
|
||||
char c = *buf;
|
||||
uint8_t M = 0;
|
||||
if(c >= '0' && c <= '9'){
|
||||
M = '0';
|
||||
}else if(c >= 'A' && c <= 'F'){
|
||||
M = 'A' - 10;
|
||||
}else if(c >= 'a' && c <= 'f'){
|
||||
M = 'a' - 10;
|
||||
}
|
||||
if(M){
|
||||
if(num & 0xf0000000){ // overflow
|
||||
*N = 0xffffff;
|
||||
return start;
|
||||
}
|
||||
num <<= 4;
|
||||
num += c - M;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
++buf;
|
||||
}
|
||||
*N = num;
|
||||
return buf;
|
||||
}
|
||||
// read octal number (without 0 prefix!)
|
||||
static char *getoct(char *buf, uint32_t *N){
|
||||
char *start = (char*)buf;
|
||||
uint32_t num = 0;
|
||||
while(*buf){
|
||||
char c = *buf;
|
||||
if(c < '0' || c > '7'){
|
||||
break;
|
||||
}
|
||||
if(num & 0xe0000000){ // overflow
|
||||
*N = 0xffffff;
|
||||
return start;
|
||||
}
|
||||
num <<= 3;
|
||||
num += c - '0';
|
||||
++buf;
|
||||
}
|
||||
*N = num;
|
||||
return buf;
|
||||
}
|
||||
// read binary number (without b prefix!)
|
||||
static char *getbin(char *buf, uint32_t *N){
|
||||
char *start = (char*)buf;
|
||||
uint32_t num = 0;
|
||||
while(*buf){
|
||||
char c = *buf;
|
||||
if(c < '0' || c > '1'){
|
||||
break;
|
||||
}
|
||||
if(num & 0x80000000){ // overflow
|
||||
*N = 0xffffff;
|
||||
return start;
|
||||
}
|
||||
num <<= 1;
|
||||
if(c == '1') num |= 1;
|
||||
++buf;
|
||||
}
|
||||
*N = num;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief getnum - read uint32_t from string (dec, hex or bin: 127, 0x7f, 0b1111111)
|
||||
* @param buf - buffer with number and so on
|
||||
* @param N - the number read
|
||||
* @return pointer to first non-number symbol in buf
|
||||
* (if it is == buf, there's no number or if *N==0xffffffff there was overflow)
|
||||
*/
|
||||
char *getnum(char *txt, uint32_t *N){
|
||||
char *nxt = NULL;
|
||||
char *s = omit_spaces(txt);
|
||||
if(*s == '0'){ // hex, oct or 0
|
||||
if(s[1] == 'x' || s[1] == 'X'){ // hex
|
||||
nxt = gethex(s+2, N);
|
||||
if(nxt == s+2) nxt = (char*)txt;
|
||||
}else if(s[1] > '0'-1 && s[1] < '8'){ // oct
|
||||
nxt = getoct(s+1, N);
|
||||
if(nxt == s+1) nxt = (char*)txt;
|
||||
}else{ // 0
|
||||
nxt = s+1;
|
||||
*N = 0;
|
||||
}
|
||||
}else if(*s == 'b' || *s == 'B'){
|
||||
nxt = getbin(s+1, N);
|
||||
if(nxt == s+1) nxt = (char*)txt;
|
||||
}else{
|
||||
nxt = getdec(s, N);
|
||||
if(nxt == s) nxt = (char*)txt;
|
||||
}
|
||||
return nxt;
|
||||
}
|
||||
|
||||
// get signed integer
|
||||
char *getint(char *txt, int32_t *I){
|
||||
char *s = omit_spaces(txt);
|
||||
int32_t sign = 1;
|
||||
uint32_t U;
|
||||
if(*s == '-'){
|
||||
sign = -1;
|
||||
++s;
|
||||
}
|
||||
char *nxt = getnum(s, &U);
|
||||
if(nxt == s) return txt;
|
||||
if(U & 0x80000000) return txt; // overfull
|
||||
*I = sign * (int32_t)U;
|
||||
return nxt;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* This file is part of the ir-allsky project.
|
||||
* This file is part of the encoders project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@@ -18,13 +18,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
extern const char *const Timage;
|
||||
#include <stdint.h>
|
||||
|
||||
#define SEND_USB (1)
|
||||
#define SEND_USART (0)
|
||||
void hexdump(int (*sendfun)(const char*), uint8_t *arr, uint16_t len);
|
||||
char *u2str(uint32_t val);
|
||||
char *i2str(int32_t i);
|
||||
char *uhex2str(uint32_t val);
|
||||
char *gethex(const char *buf, uint32_t *N);
|
||||
char *getnum(char *txt, uint32_t *N);
|
||||
char *omit_spaces(char *buf);
|
||||
char *getint(char *txt, int32_t *I);
|
||||
|
||||
extern uint8_t cartoon;
|
||||
void chsendfun(int sendto);
|
||||
const char *parse_cmd(char *buf, int sendto);
|
||||
void dumpIma(const fp_t im[MLX_PIXNO]);
|
||||
void drawIma(const fp_t im[MLX_PIXNO]);
|
||||
229
F1:F103/AS3935-lightning/usb_descr.c
Normal file
229
F1:F103/AS3935-lightning/usb_descr.c
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h> // memcpy
|
||||
|
||||
#include "flash.h" // MAX_IINTERFACE_SZ, the_conf
|
||||
#include "usb_descr.h"
|
||||
|
||||
// low/high for uint16_t
|
||||
#define L16(x) (x & 0xff)
|
||||
#define H16(x) (x >> 8)
|
||||
|
||||
static const uint8_t USB_DeviceDescriptor[] = {
|
||||
USB_DT_DEVICE_SIZE, // bLength
|
||||
USB_DT_DEVICE, // bDescriptorType
|
||||
L16(bcdUSB), // bcdUSB_L
|
||||
H16(bcdUSB), // bcdUSB_H
|
||||
USB_CLASS_MISC, // bDeviceClass
|
||||
bDeviceSubClass, // bDeviceSubClass
|
||||
bDeviceProtocol, // bDeviceProtocol
|
||||
USB_EP0BUFSZ, // bMaxPacketSize
|
||||
L16(idVendor), // idVendor_L
|
||||
H16(idVendor), // idVendor_H
|
||||
L16(idProduct), // idProduct_L
|
||||
H16(idProduct), // idProduct_H
|
||||
L16(bcdDevice_Ver), // bcdDevice_Ver_L
|
||||
H16(bcdDevice_Ver), // bcdDevice_Ver_H
|
||||
iMANUFACTURER_DESCR, // iManufacturer - indexes of string descriptors in array
|
||||
iPRODUCT_DESCR, // iProduct
|
||||
iSERIAL_DESCR, // iSerial
|
||||
bNumConfigurations // bNumConfigurations
|
||||
};
|
||||
|
||||
static const uint8_t USB_DeviceQualifierDescriptor[] = {
|
||||
USB_DT_QUALIFIER_SIZE, //bLength
|
||||
USB_DT_QUALIFIER, // bDescriptorType
|
||||
L16(bcdUSB), // bcdUSB_L
|
||||
H16(bcdUSB), // bcdUSB_H
|
||||
USB_CLASS_PER_INTERFACE, // bDeviceClass
|
||||
bDeviceSubClass, // bDeviceSubClass
|
||||
bDeviceProtocol, // bDeviceProtocol
|
||||
USB_EP0BUFSZ, // bMaxPacketSize0
|
||||
bNumConfigurations, // bNumConfigurations
|
||||
0 // Reserved
|
||||
};
|
||||
|
||||
#define wTotalLength (USB_DT_CONFIG_SIZE + (bNumInterfaces * USB_DT_INTERFACE_SIZE) + (bTotNumEndpoints * USB_DT_ENDPOINT_SIZE) + (bNumCsInterfaces * USB_DT_CS_INTERFACE_SIZE) - 1)
|
||||
|
||||
static const uint8_t USB_ConfigDescriptor[] = {
|
||||
// Configuration Descriptor
|
||||
USB_DT_CONFIG_SIZE, // bLength: Configuration Descriptor size
|
||||
USB_DT_CONFIG, // bDescriptorType: Configuration
|
||||
L16(wTotalLength), // wTotalLength.L :no of returned bytes
|
||||
H16(wTotalLength), // wTotalLength.H
|
||||
bNumInterfaces, // bNumInterfaces
|
||||
1, // bConfigurationValue: Current configuration value
|
||||
0, // iConfiguration: Index of string descriptor describing the configuration or 0
|
||||
BusPowered, // bmAttributes - Bus powered
|
||||
50, // MaxPower in 2mA units
|
||||
//---------------------------------------------------------------------------
|
||||
// Virtual command Interface Descriptor
|
||||
USB_DT_INTERFACE_SIZE, // bLength: Interface Descriptor size
|
||||
USB_DT_INTERFACE, // bDescriptorType: Interface
|
||||
0, // bInterfaceNumber: Number of Interface
|
||||
0, // bAlternateSetting: Alternate setting
|
||||
1, // bNumEndpoints: one for this
|
||||
USB_CLASS_COMM, // bInterfaceClass
|
||||
2, // bInterfaceSubClass: ACM
|
||||
1, // bInterfaceProtocol: Common AT commands
|
||||
iINTERFACE_DESCR1, // iInterface
|
||||
// ---- CS Interfaces
|
||||
USB_DT_CS_INTERFACE_SIZE, // bLength
|
||||
USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE
|
||||
0, // bDescriptorSubtype: Header Func Desc
|
||||
0x10, // bcdCDC: spec release number
|
||||
1, // bDataInterface
|
||||
USB_DT_CS_INTERFACE_SIZE, // bLength
|
||||
USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE
|
||||
1, // bDescriptorSubtype: Call Management Func Desc
|
||||
0, // bmCapabilities: D0+D1
|
||||
1, // bDataInterface
|
||||
USB_DT_CS_INTERFACE_SIZE-1, // bLength
|
||||
USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE
|
||||
2, // bDescriptorSubtype: Abstract Control Management desc
|
||||
2, // bmCapabilities
|
||||
USB_DT_CS_INTERFACE_SIZE, // bLength
|
||||
USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE
|
||||
6, // bDescriptorSubtype: Union func desc
|
||||
0, // bMasterInterface: Communication class interface
|
||||
1, // bSlaveInterface0: Data Class Interface
|
||||
// Virtual endpoint 1 Descriptor
|
||||
USB_DT_ENDPOINT_SIZE, // bLength: Endpoint Descriptor size
|
||||
USB_DT_ENDPOINT, // bDescriptorType: Endpoint
|
||||
0x8A, // bEndpointAddress IN10
|
||||
USB_BM_ATTR_INTERRUPT, // bmAttributes: Interrupt
|
||||
L16(USB_EP1BUFSZ), // wMaxPacketSize LO
|
||||
H16(USB_EP1BUFSZ), // wMaxPacketSize HI
|
||||
0x10, // bInterval: 16ms
|
||||
//---------------------------------------------------------------------------
|
||||
// Data interface
|
||||
USB_DT_INTERFACE_SIZE, // bLength: Interface Descriptor size
|
||||
USB_DT_INTERFACE, // bDescriptorType: Interface
|
||||
1, // bInterfaceNumber: Number of Interface
|
||||
0, // bAlternateSetting: Alternate setting
|
||||
2, // bNumEndpoints: in and out
|
||||
USB_CLASS_DATA, // bInterfaceClass
|
||||
2, // bInterfaceSubClass: ACM
|
||||
0, // bInterfaceProtocol
|
||||
0, // iInterface
|
||||
//Endpoint IN1 Descriptor
|
||||
USB_DT_ENDPOINT_SIZE, // bLength: Endpoint Descriptor size
|
||||
USB_DT_ENDPOINT, // bDescriptorType: Endpoint
|
||||
0x81, // bEndpointAddress: IN1
|
||||
USB_BM_ATTR_BULK, // bmAttributes: Bulk
|
||||
L16(USB_TXBUFSZ), // wMaxPacketSize LO
|
||||
H16(USB_TXBUFSZ), // wMaxPacketSize HI
|
||||
0, // bInterval: ignore for Bulk transfer
|
||||
// Endpoint OUT1 Descriptor
|
||||
USB_DT_ENDPOINT_SIZE, // bLength: Endpoint Descriptor size
|
||||
USB_DT_ENDPOINT, // bDescriptorType: Endpoint
|
||||
0x01, // bEndpointAddress: OUT1
|
||||
USB_BM_ATTR_BULK, // bmAttributes: Bulk
|
||||
L16(USB_RXBUFSZ), // wMaxPacketSize LO
|
||||
H16(USB_RXBUFSZ), // wMaxPacketSize HI
|
||||
0, // bInterval: ignore for Bulk transfer
|
||||
|
||||
};
|
||||
|
||||
//const uint8_t HID_ReportDescriptor[];
|
||||
|
||||
_USB_LANG_ID_(LD, LANG_US);
|
||||
_USB_STRING_(SD, u"0.0.1");
|
||||
_USB_STRING_(MD, u"eddy@sao.ru");
|
||||
_USB_STRING_(PD, u"AS3935 lightning detector");
|
||||
|
||||
// iInterface will change on initialisation by config
|
||||
#define _USB_IIDESCR_(str) {sizeof(str), 0x03, str}
|
||||
typedef struct{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t bString[MAX_IINTERFACE_SZ];
|
||||
}iidescr_t;
|
||||
static iidescr_t iid = _USB_IIDESCR_(u"lightning_det");
|
||||
|
||||
static const void* const StringDescriptor[iDESCR_AMOUNT] = {
|
||||
[iLANGUAGE_DESCR] = &LD,
|
||||
[iMANUFACTURER_DESCR] = &MD,
|
||||
[iPRODUCT_DESCR] = &PD,
|
||||
[iSERIAL_DESCR] = &SD,
|
||||
[iINTERFACE_DESCR1] = &iid
|
||||
};
|
||||
|
||||
static void wr0(const uint8_t *buf, uint16_t size, uint16_t askedsize){
|
||||
if(askedsize < size) size = askedsize; // shortened request
|
||||
if(size < USB_EP0BUFSZ){
|
||||
EP_WriteIRQ(0, buf, size);
|
||||
return;
|
||||
}
|
||||
while(size){
|
||||
uint16_t l = size;
|
||||
if(l > USB_EP0BUFSZ) l = USB_EP0BUFSZ;
|
||||
EP_WriteIRQ(0, buf, l);
|
||||
buf += l;
|
||||
size -= l;
|
||||
uint8_t needzlp = (l == USB_EP0BUFSZ) ? 1 : 0;
|
||||
if(size || needzlp){ // send last data buffer
|
||||
uint16_t epstatus = KEEP_DTOG(USB->EPnR[0]);
|
||||
// keep DTOGs, clear CTR_RX,TX, set TX VALID, leave stat_Rx
|
||||
USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX|USB_EPnR_STAT_RX))
|
||||
^ USB_EPnR_STAT_TX;
|
||||
uint32_t ctr = 1000000;
|
||||
while(--ctr && (USB->ISTR & USB_ISTR_CTR) == 0){IWDG->KR = IWDG_REFRESH;};
|
||||
if((USB->ISTR & USB_ISTR_CTR) == 0){
|
||||
return;
|
||||
}
|
||||
if(needzlp) EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void get_descriptor(config_pack_t *pack){
|
||||
uint8_t descrtype = pack->wValue >> 8,
|
||||
descridx = pack->wValue & 0xff;
|
||||
switch(descrtype){
|
||||
case DEVICE_DESCRIPTOR:
|
||||
wr0(USB_DeviceDescriptor, sizeof(USB_DeviceDescriptor), pack->wLength);
|
||||
break;
|
||||
case CONFIGURATION_DESCRIPTOR:
|
||||
wr0(USB_ConfigDescriptor, sizeof(USB_ConfigDescriptor), pack->wLength);
|
||||
break;
|
||||
case STRING_DESCRIPTOR:
|
||||
if(descridx < iDESCR_AMOUNT){
|
||||
wr0((const uint8_t *)StringDescriptor[descridx], *((uint8_t*)StringDescriptor[descridx]), pack->wLength);
|
||||
}else{
|
||||
EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
break;
|
||||
case DEVICE_QUALIFIER_DESCRIPTOR:
|
||||
wr0(USB_DeviceQualifierDescriptor, sizeof(USB_DeviceQualifierDescriptor), pack->wLength);
|
||||
break;
|
||||
/* case HID_REPORT_DESCRIPTOR:
|
||||
wr0(HID_ReportDescriptor, sizeof(HID_ReportDescriptor), pack->wLength);
|
||||
break;*/
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setup_interfaces(){
|
||||
if(the_conf.iIlength){
|
||||
iid.bLength = the_conf.iIlength + 2; // +2 - for bLength and bDescriptorType
|
||||
memcpy(iid.bString, the_conf.iInterface, the_conf.iIlength);
|
||||
}
|
||||
iid.bDescriptorType = 0x03;
|
||||
}
|
||||
63
F1:F103/AS3935-lightning/usb_descr.h
Normal file
63
F1:F103/AS3935-lightning/usb_descr.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usb_lib.h"
|
||||
|
||||
// definition of parts common for USB_DeviceDescriptor & USB_DeviceQualifierDescriptor
|
||||
// bcdUSB: 1.10
|
||||
#define bcdUSB 0x0110
|
||||
// Class - Misc (EF), subclass - common (2), protocol - interface association descr (1)
|
||||
#define bDeviceSubClass 0x02
|
||||
#define bDeviceProtocol 0x01
|
||||
#define idVendor 0x0483
|
||||
#define idProduct 0x5740
|
||||
#define bcdDevice_Ver 0x0200
|
||||
#define bNumConfigurations 1
|
||||
|
||||
// amount of interfaces and endpoints (except 0) used
|
||||
#define bNumInterfaces 2
|
||||
#define bTotNumEndpoints 3
|
||||
#define bNumCsInterfaces 4
|
||||
|
||||
// powered
|
||||
#define BusPowered (1<<7)
|
||||
#define SelfPowered (1<<6)
|
||||
#define RemoteWakeup (1<<5)
|
||||
|
||||
// buffer sizes
|
||||
// for USB FS EP0 buffers are from 8 to 64 bytes long
|
||||
#define USB_EP0BUFSZ 64
|
||||
#define USB_EP1BUFSZ 10
|
||||
// Rx/Tx EPs
|
||||
#define USB_RXBUFSZ 64
|
||||
#define USB_TXBUFSZ 64
|
||||
|
||||
// string descriptors
|
||||
enum{
|
||||
iLANGUAGE_DESCR,
|
||||
iMANUFACTURER_DESCR,
|
||||
iPRODUCT_DESCR,
|
||||
iSERIAL_DESCR,
|
||||
iINTERFACE_DESCR1,
|
||||
iDESCR_AMOUNT
|
||||
};
|
||||
|
||||
void get_descriptor(config_pack_t *pack);
|
||||
void setup_interfaces();
|
||||
252
F1:F103/AS3935-lightning/usb_dev.c
Normal file
252
F1:F103/AS3935-lightning/usb_dev.c
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "ringbuffer.h"
|
||||
#include "usb_descr.h"
|
||||
#include "usb_dev.h"
|
||||
|
||||
// Class-Specific Control Requests
|
||||
#define SEND_ENCAPSULATED_COMMAND 0x00 // unused
|
||||
#define GET_ENCAPSULATED_RESPONSE 0x01 // unused
|
||||
#define SET_COMM_FEATURE 0x02 // unused
|
||||
#define GET_COMM_FEATURE 0x03 // unused
|
||||
#define CLEAR_COMM_FEATURE 0x04 // unused
|
||||
#define SET_LINE_CODING 0x20
|
||||
#define GET_LINE_CODING 0x21
|
||||
#define SET_CONTROL_LINE_STATE 0x22
|
||||
#define SEND_BREAK 0x23
|
||||
|
||||
// control line states
|
||||
#define CONTROL_DTR 0x01
|
||||
#define CONTROL_RTS 0x02
|
||||
|
||||
// inbuf overflow when receiving
|
||||
static volatile uint8_t bufovrfl = 0;
|
||||
|
||||
// receive buffer: hold data until chkin() call
|
||||
static uint8_t volatile rcvbuf[USB_RXBUFSZ] __attribute__((aligned(4)));
|
||||
static uint8_t volatile rcvbuflen = 0;
|
||||
// line coding
|
||||
usb_LineCoding lineCoding = {115200, 0, 0, 8};
|
||||
// CDC configured and ready to use
|
||||
volatile uint8_t CDCready = 0;
|
||||
|
||||
// ring buffers for incoming and outgoing data
|
||||
static uint8_t obuf[RBOUTSZ], ibuf[RBINSZ];
|
||||
static volatile ringbuffer rbout = {.data = obuf, .length = RBOUTSZ, .head = 0, .tail = 0};
|
||||
static volatile ringbuffer rbin = {.data = ibuf, .length = RBINSZ, .head = 0, .tail = 0};
|
||||
// last send data size
|
||||
static volatile int lastdsz = 0;
|
||||
|
||||
static void chkin(){
|
||||
if(bufovrfl) return; // allow user to know that previous buffer was overflowed and cleared
|
||||
if(!rcvbuflen) return;
|
||||
int w = RB_write((ringbuffer*)&rbin, (uint8_t*)rcvbuf, rcvbuflen);
|
||||
if(w < 0){
|
||||
return;
|
||||
}
|
||||
if(w != rcvbuflen) bufovrfl = 1;
|
||||
rcvbuflen = 0;
|
||||
uint16_t status = KEEP_DTOG(USB->EPnR[1]); // don't change DTOG
|
||||
USB->EPnR[1] = (status & ~(USB_EPnR_STAT_TX|USB_EPnR_CTR_RX)) ^ USB_EPnR_STAT_RX; // prepare to get next data portion
|
||||
}
|
||||
|
||||
// called from transmit EP to send next data portion or by user - when new transmission starts
|
||||
static void send_next(){
|
||||
uint8_t usbbuff[USB_TXBUFSZ] __attribute__((aligned(4)));
|
||||
int buflen = RB_read((ringbuffer*)&rbout, (uint8_t*)usbbuff, USB_TXBUFSZ);
|
||||
if(buflen == 0){
|
||||
if(lastdsz == USB_TXBUFSZ) EP_Write(1, NULL, 0); // send ZLP after USB_TXBUFSZ bits packet when nothing more to send
|
||||
lastdsz = 0;
|
||||
return;
|
||||
}else if(buflen < 0){
|
||||
lastdsz = 0;
|
||||
return;
|
||||
}
|
||||
EP_Write(1, (uint8_t*)usbbuff, buflen);
|
||||
lastdsz = buflen;
|
||||
}
|
||||
|
||||
// data IN/OUT handler
|
||||
static void rxtx_handler(){
|
||||
uint16_t epstatus = KEEP_DTOG(USB->EPnR[1]);
|
||||
if(RX_FLAG(epstatus)){ // receive data
|
||||
if(rcvbuflen){
|
||||
bufovrfl = 1; // lost last data
|
||||
rcvbuflen = 0;
|
||||
}
|
||||
rcvbuflen = EP_Read(1, (uint8_t*)rcvbuf);
|
||||
USB->EPnR[1] = epstatus & ~(USB_EPnR_CTR_RX | USB_EPnR_STAT_RX | USB_EPnR_STAT_TX); // keep RX in STALL state until read data
|
||||
chkin(); // try to write current data into RXbuf if it's not busy
|
||||
}else{ // tx successfull
|
||||
USB->EPnR[1] = (epstatus & ~(USB_EPnR_CTR_TX | USB_EPnR_STAT_TX)) ^ USB_EPnR_STAT_RX;
|
||||
send_next();
|
||||
}
|
||||
}
|
||||
|
||||
// weak handlers: change them somewhere else if you want to setup USART
|
||||
// SET_LINE_CODING
|
||||
void linecoding_handler(usb_LineCoding *lc){
|
||||
lineCoding = *lc;
|
||||
}
|
||||
|
||||
// SET_CONTROL_LINE_STATE
|
||||
void clstate_handler(uint16_t val){
|
||||
CDCready = val; // CONTROL_DTR | CONTROL_RTS -> interface connected; 0 -> disconnected
|
||||
}
|
||||
|
||||
// SEND_BREAK
|
||||
void break_handler(){
|
||||
CDCready = 0;
|
||||
}
|
||||
|
||||
// USB is configured: setup endpoints
|
||||
void set_configuration(){
|
||||
EP_Init(1, EP_TYPE_BULK, USB_TXBUFSZ, USB_RXBUFSZ, rxtx_handler); // IN1 and OUT1
|
||||
}
|
||||
|
||||
// PL2303 CLASS request
|
||||
void usb_class_request(config_pack_t *req, uint8_t *data, uint16_t datalen){
|
||||
uint8_t recipient = REQUEST_RECIPIENT(req->bmRequestType);
|
||||
uint8_t dev2host = (req->bmRequestType & 0x80) ? 1 : 0;
|
||||
switch(recipient){
|
||||
case REQ_RECIPIENT_INTERFACE:
|
||||
switch(req->bRequest){
|
||||
case SET_LINE_CODING:
|
||||
if(!data || !datalen) break; // wait for data
|
||||
if(datalen == sizeof(usb_LineCoding))
|
||||
linecoding_handler((usb_LineCoding*)data);
|
||||
break;
|
||||
case GET_LINE_CODING:
|
||||
EP_WriteIRQ(0, (uint8_t*)&lineCoding, sizeof(lineCoding));
|
||||
break;
|
||||
case SET_CONTROL_LINE_STATE:
|
||||
clstate_handler(req->wValue);
|
||||
break;
|
||||
case SEND_BREAK:
|
||||
break_handler();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if(dev2host) EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
if(!dev2host) EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
|
||||
// blocking send full content of ring buffer
|
||||
int USB_sendall(){
|
||||
while(lastdsz > 0){
|
||||
if(!CDCready) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// put `buf` into queue to send
|
||||
int USB_send(const uint8_t *buf, int len){
|
||||
if(!buf || !CDCready || !len) return FALSE;
|
||||
while(len){
|
||||
IWDG->KR = IWDG_REFRESH;
|
||||
int l = RB_datalen((ringbuffer*)&rbout);
|
||||
if(l < 0) continue;
|
||||
int portion = rbout.length - 1 - l;
|
||||
if(portion < 1){
|
||||
if(lastdsz == 0) send_next();
|
||||
continue;
|
||||
}
|
||||
if(portion > len) portion = len;
|
||||
int a = RB_write((ringbuffer*)&rbout, buf, portion);
|
||||
if(a > 0){
|
||||
len -= a;
|
||||
buf += a;
|
||||
} else if (a < 0) continue; // do nothing if buffer is in reading state
|
||||
if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int USB_putbyte(uint8_t byte){
|
||||
if(!CDCready) return FALSE;
|
||||
int l = 0;
|
||||
while((l = RB_write((ringbuffer*)&rbout, &byte, 1)) != 1){
|
||||
if(l < 0) continue;
|
||||
}
|
||||
if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int USB_sendstr(const char *string){
|
||||
if(!string || !CDCready) return FALSE;
|
||||
int len = 0;
|
||||
const char *b = string;
|
||||
while(*b++) ++len;
|
||||
if(!len) return FALSE;
|
||||
return USB_send((const uint8_t*)string, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USB_receive - get binary data from receiving ring-buffer
|
||||
* @param buf (i) - buffer for received data
|
||||
* @param len - length of `buf`
|
||||
* @return amount of received bytes (negative, if overfull happened)
|
||||
*/
|
||||
int USB_receive(uint8_t *buf, int len){
|
||||
chkin();
|
||||
if(bufovrfl){
|
||||
while(1 != RB_clearbuf((ringbuffer*)&rbin));
|
||||
bufovrfl = 0;
|
||||
return -1;
|
||||
}
|
||||
int sz = RB_read((ringbuffer*)&rbin, buf, len);
|
||||
if(sz < 0) return 0; // buffer in writting state
|
||||
return sz;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USB_receivestr - get string up to '\n' and replace '\n' with 0
|
||||
* @param buf - receiving buffer
|
||||
* @param len - its length
|
||||
* @return strlen or negative value indicating overflow
|
||||
*/
|
||||
int USB_receivestr(char *buf, int len){
|
||||
chkin();
|
||||
if(bufovrfl){
|
||||
while(1 != RB_clearbuf((ringbuffer*)&rbin));
|
||||
bufovrfl = 0;
|
||||
return -1;
|
||||
}
|
||||
int l = RB_datalento((ringbuffer*)&rbin, '\n');
|
||||
if(l > len){ // can't read: line too long -> clear it
|
||||
RB_readto((ringbuffer*)&rbin, '\n', NULL, 0);
|
||||
return -1;
|
||||
}else if(l < 1){ // nothing or no '\n' ?
|
||||
if(rbin.length == RB_datalen((ringbuffer*)&rbin)){ // buffer is full but no '\n' found
|
||||
while(1 != RB_clearbuf((ringbuffer*)&rbin));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
l = RB_readto((ringbuffer*)&rbin, '\n', (uint8_t*)buf, len);
|
||||
if(l == 0) return 0;
|
||||
buf[l-1] = 0; // replace '\n' with strend
|
||||
return l;
|
||||
}
|
||||
|
||||
57
F1:F103/AS3935-lightning/usb_dev.h
Normal file
57
F1:F103/AS3935-lightning/usb_dev.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "usb_lib.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t dwDTERate;
|
||||
uint8_t bCharFormat;
|
||||
#define USB_CDC_1_STOP_BITS 0
|
||||
#define USB_CDC_1_5_STOP_BITS 1
|
||||
#define USB_CDC_2_STOP_BITS 2
|
||||
uint8_t bParityType;
|
||||
#define USB_CDC_NO_PARITY 0
|
||||
#define USB_CDC_ODD_PARITY 1
|
||||
#define USB_CDC_EVEN_PARITY 2
|
||||
#define USB_CDC_MARK_PARITY 3
|
||||
#define USB_CDC_SPACE_PARITY 4
|
||||
uint8_t bDataBits;
|
||||
} __attribute__ ((packed)) usb_LineCoding;
|
||||
|
||||
extern usb_LineCoding lineCoding;
|
||||
extern volatile uint8_t CDCready;
|
||||
|
||||
void break_handler();
|
||||
void clstate_handler(uint16_t val);
|
||||
void linecoding_handler(usb_LineCoding *lc);
|
||||
|
||||
|
||||
// sizes of ringbuffers for outgoing and incoming data
|
||||
#define RBOUTSZ (1024)
|
||||
#define RBINSZ (1024)
|
||||
|
||||
#define newline() USB_putbyte('\n')
|
||||
#define USND(s) do{USB_sendstr(s); USB_putbyte('\n');}while(0)
|
||||
|
||||
int USB_sendall();
|
||||
int USB_send(const uint8_t *buf, int len);
|
||||
int USB_putbyte(uint8_t byte);
|
||||
int USB_sendstr(const char *string);
|
||||
int USB_receive(uint8_t *buf, int len);
|
||||
int USB_receivestr(char *buf, int len);
|
||||
439
F1:F103/AS3935-lightning/usb_lib.c
Normal file
439
F1:F103/AS3935-lightning/usb_lib.c
Normal file
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usb_lib.h"
|
||||
#include "usb_descr.h"
|
||||
#include "usb_dev.h"
|
||||
|
||||
static ep_t endpoints[STM32ENDPOINTS];
|
||||
|
||||
static uint16_t USB_Addr = 0;
|
||||
static uint8_t setupdatabuf[EP0DATABUF_SIZE] __attribute__((aligned(4)));
|
||||
static config_pack_t *setup_packet = (config_pack_t*) setupdatabuf;
|
||||
volatile uint8_t usbON = 0; // device is configured and active
|
||||
|
||||
static uint16_t configuration = 0; // reply for GET_CONFIGURATION (==1 if configured)
|
||||
static inline void std_d2h_req(){
|
||||
uint16_t st = 0;
|
||||
switch(setup_packet->bRequest){
|
||||
case GET_DESCRIPTOR:
|
||||
get_descriptor(setup_packet);
|
||||
break;
|
||||
case GET_STATUS:
|
||||
EP_WriteIRQ(0, (uint8_t *)&st, 2); // send status: Bus Powered
|
||||
break;
|
||||
case GET_CONFIGURATION:
|
||||
EP_WriteIRQ(0, (uint8_t*)&configuration, 1);
|
||||
break;
|
||||
default:
|
||||
EP_WriteIRQ(0, NULL, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void std_h2d_req(){
|
||||
switch(setup_packet->bRequest){
|
||||
case SET_ADDRESS:
|
||||
// new address will be assigned later - after acknowlegement or request to host
|
||||
USB_Addr = setup_packet->wValue;
|
||||
break;
|
||||
case SET_CONFIGURATION:
|
||||
// Now device configured
|
||||
configuration = setup_packet->wValue;
|
||||
set_configuration();
|
||||
usbON = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WEAK usb_standard_request(){
|
||||
uint8_t recipient = REQUEST_RECIPIENT(setup_packet->bmRequestType);
|
||||
uint8_t dev2host = (setup_packet->bmRequestType & 0x80) ? 1 : 0;
|
||||
switch(recipient){
|
||||
case REQ_RECIPIENT_DEVICE:
|
||||
if(dev2host){
|
||||
std_d2h_req();
|
||||
}else{
|
||||
std_h2d_req();
|
||||
}
|
||||
break;
|
||||
case REQ_RECIPIENT_INTERFACE:
|
||||
if(dev2host && setup_packet->bRequest == GET_DESCRIPTOR){
|
||||
get_descriptor(setup_packet);
|
||||
}
|
||||
break;
|
||||
case REQ_RECIPIENT_ENDPOINT:
|
||||
if(setup_packet->bRequest == CLEAR_FEATURE){
|
||||
}else{ /* wrong */ }
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(!dev2host) EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
|
||||
void WEAK usb_class_request(config_pack_t *req, uint8_t _U_ *data, uint16_t _U_ datalen){
|
||||
switch(req->bRequest){
|
||||
case GET_INTERFACE:
|
||||
break;
|
||||
case SET_CONFIGURATION: // set featuring by req->wValue
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(0 == (setup_packet->bmRequestType & 0x80)) // host2dev
|
||||
EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
|
||||
void WEAK usb_vendor_request(config_pack_t _U_ *packet, uint8_t _U_ *data, uint16_t _U_ datalen){
|
||||
if(0 == (setup_packet->bmRequestType & 0x80)) // host2dev
|
||||
EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
bmRequestType: 76543210
|
||||
7 direction: 0 - host->device, 1 - device->host
|
||||
65 type: 0 - standard, 1 - class, 2 - vendor
|
||||
4..0 getter: 0 - device, 1 - interface, 2 - endpoint, 3 - other
|
||||
*/
|
||||
/**
|
||||
* Endpoint0 (control) handler
|
||||
*/
|
||||
static void EP0_Handler(){
|
||||
uint8_t ep0dbuflen = 0;
|
||||
uint8_t ep0databuf[EP0DATABUF_SIZE] __attribute__((aligned(4)));
|
||||
uint16_t epstatus = KEEP_DTOG(USB->EPnR[0]); // EP0R on input -> return this value after modifications
|
||||
int rxflag = RX_FLAG(epstatus);
|
||||
// check direction
|
||||
if(USB->ISTR & USB_ISTR_DIR){ // OUT interrupt - receive data, CTR_RX==1 (if CTR_TX == 1 - two pending transactions: receive following by transmit)
|
||||
if(epstatus & USB_EPnR_SETUP){ // setup packet -> copy data to conf_pack
|
||||
EP_Read(0, setupdatabuf);
|
||||
// interrupt handler will be called later
|
||||
}else if(epstatus & USB_EPnR_CTR_RX){ // data packet -> push received data to ep0databuf
|
||||
//if(endpoints[0].rx_cnt){ }
|
||||
ep0dbuflen = EP_Read(0, ep0databuf);
|
||||
}
|
||||
}
|
||||
if(rxflag){
|
||||
uint8_t reqtype = REQUEST_TYPE(setup_packet->bmRequestType);
|
||||
switch(reqtype){
|
||||
case REQ_TYPE_STANDARD:
|
||||
if(SETUP_FLAG(epstatus)){
|
||||
usb_standard_request();
|
||||
}else{ }
|
||||
break;
|
||||
case REQ_TYPE_CLASS:
|
||||
usb_class_request(setup_packet, ep0databuf, ep0dbuflen);
|
||||
break;
|
||||
case REQ_TYPE_VENDOR:
|
||||
usb_vendor_request(setup_packet, ep0databuf, ep0dbuflen);
|
||||
break;
|
||||
default:
|
||||
EP_WriteIRQ(0, NULL, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(TX_FLAG(epstatus)){
|
||||
// now we can change address after enumeration
|
||||
if ((USB->DADDR & USB_DADDR_ADD) != USB_Addr){
|
||||
USB->DADDR = USB_DADDR_EF | USB_Addr;
|
||||
usbON = 0;
|
||||
}
|
||||
}
|
||||
//epstatus = KEEP_DTOG(USB->EPnR[0]);
|
||||
if(rxflag) epstatus ^= USB_EPnR_STAT_TX; // start ZLP or data transmission
|
||||
else epstatus &= ~USB_EPnR_STAT_TX; // or leave unchanged
|
||||
// keep DTOGs, clear CTR_RX,TX, set RX VALID
|
||||
USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX)) ^ USB_EPnR_STAT_RX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to EP buffer (called from IRQ handler)
|
||||
* @param number - EP number
|
||||
* @param *buf - array with data
|
||||
* @param size - its size
|
||||
*/
|
||||
void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){
|
||||
if(size > endpoints[number].txbufsz) size = endpoints[number].txbufsz;
|
||||
#ifndef USB32
|
||||
uint16_t N2 = (size + 1) >> 1;
|
||||
// the buffer is 16-bit, so we should copy data as it would be uint16_t
|
||||
uint16_t *buf16 = (uint16_t *)buf;
|
||||
#else
|
||||
int N4 = (size + 3) >> 2;
|
||||
uint32_t *buf32 = (uint32_t *)buf;
|
||||
#endif
|
||||
#if defined USB1_16
|
||||
// very bad: what if `size` is odd?
|
||||
uint32_t *out = (uint32_t *)endpoints[number].tx_buf;
|
||||
for(int i = 0; i < N2; ++i, ++out){
|
||||
*out = buf16[i];
|
||||
}
|
||||
#elif defined USB2_16
|
||||
// use memcpy instead?
|
||||
for(int i = 0; i < N2; ++i){
|
||||
endpoints[number].tx_buf[i] = buf16[i];
|
||||
}
|
||||
#elif defined USB32
|
||||
for(int i = 0; i < N4; ++i) endpoints[number].tx_buf[i] = buf32[i];
|
||||
#else
|
||||
#error "Define USB1_16 / USB2_16 / USB32"
|
||||
#endif
|
||||
#ifndef USB32
|
||||
USB_BTABLE->EP[number].USB_COUNT_TX = size;
|
||||
#else
|
||||
USB_BTABLE->EP[number].USB_ADDR_COUNT_TX = (USB_BTABLE->EP[number].USB_ADDR_COUNT_TX & 0xffff) | (size << 16);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to EP buffer (called outside IRQ handler)
|
||||
* @param number - EP number
|
||||
* @param *buf - array with data
|
||||
* @param size - its size
|
||||
*/
|
||||
void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size){
|
||||
EP_WriteIRQ(number, buf, size);
|
||||
uint16_t epstatus = KEEP_DTOG(USB->EPnR[number]);
|
||||
// keep DTOGs and RX stat, clear CTR_TX & set TX VALID to start transmission
|
||||
USB->EPnR[number] = (epstatus & ~(USB_EPnR_CTR_TX | USB_EPnR_STAT_RX)) ^ USB_EPnR_STAT_TX;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy data from EP buffer into user buffer area
|
||||
* @param *buf - user array for data
|
||||
* @return amount of data read
|
||||
*/
|
||||
int EP_Read(uint8_t number, uint8_t *buf){
|
||||
int sz = endpoints[number].rx_cnt;
|
||||
if(!sz) return 0;
|
||||
endpoints[number].rx_cnt = 0;
|
||||
#if defined USB1_16
|
||||
int n = (sz + 1) >> 1;
|
||||
uint32_t *in = (uint32_t*)endpoints[number].rx_buf;
|
||||
uint16_t *out = (uint16_t*)buf;
|
||||
for(int i = 0; i < n; ++i, ++in)
|
||||
out[i] = *(uint16_t*)in;
|
||||
#elif defined USB2_16
|
||||
// use memcpy instead?
|
||||
for(int i = 0; i < sz; ++i)
|
||||
buf[i] = endpoints[number].rx_buf[i];
|
||||
#elif defined USB32
|
||||
uint32_t *u32buf = (uint32_t*) buf;
|
||||
int N4 = (sz + 3) >> 2;
|
||||
for(int i = 0; i < N4; ++i) u32buf[i] = endpoints[number].rx_buf[i];
|
||||
#else
|
||||
#error "Define USB1_16 / USB2_16 / USB32"
|
||||
#endif
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
static uint16_t lastaddr = LASTADDR_DEFAULT;
|
||||
/**
|
||||
* Endpoint initialisation
|
||||
* @param number - EP num (0...7)
|
||||
* @param type - EP type (EP_TYPE_BULK, EP_TYPE_CONTROL, EP_TYPE_ISO, EP_TYPE_INTERRUPT)
|
||||
* @param txsz - transmission buffer size @ USB/CAN buffer
|
||||
* @param rxsz - reception buffer size @ USB/CAN buffer
|
||||
* @param uint16_t (*func)(ep_t *ep) - EP handler function
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)(ep_t ep)){
|
||||
#ifdef STM32G0
|
||||
// in STM32G0 all buffers should be aligned by 32 bits
|
||||
if(txsz & 3) txsz = ((txsz >> 2)+1) << 2;
|
||||
if(rxsz & 3) rxsz = ((rxsz >> 2)+1) << 2;
|
||||
#endif
|
||||
if(number >= STM32ENDPOINTS) return 4; // out of configured amount
|
||||
if(txsz > USB_BTABLE_SIZE/ACCESSZ || rxsz > USB_BTABLE_SIZE/ACCESSZ) return 1; // buffer too large
|
||||
if(lastaddr + txsz + rxsz >= USB_BTABLE_SIZE/ACCESSZ) return 2; // out of btable
|
||||
USB->EPnR[number] = (type << 9) | (number & USB_EPnR_EA);
|
||||
USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX;
|
||||
if(rxsz & 1) return 3; // wrong rx buffer size
|
||||
uint16_t countrx = 0;
|
||||
if(rxsz < 64) countrx = rxsz / 2;
|
||||
else{
|
||||
if(rxsz & 0x1f) return 3; // should be multiple of 32
|
||||
countrx = 31 + rxsz / 32;
|
||||
}
|
||||
#ifdef USB32
|
||||
endpoints[number].tx_buf = (uint32_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
|
||||
#else
|
||||
endpoints[number].tx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
|
||||
#endif
|
||||
endpoints[number].txbufsz = txsz;
|
||||
#ifdef USB32
|
||||
USB_BTABLE->EP[number].USB_ADDR_COUNT_TX = (uint32_t) lastaddr;
|
||||
#else
|
||||
USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr;
|
||||
USB_BTABLE->EP[number].USB_COUNT_TX = 0;
|
||||
#endif
|
||||
lastaddr += txsz;
|
||||
#ifdef USB32
|
||||
endpoints[number].rx_buf = (uint32_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
|
||||
USB_BTABLE->EP[number].USB_ADDR_COUNT_RX = (uint32_t) lastaddr | countrx << 26;
|
||||
#else
|
||||
endpoints[number].rx_buf = (uint8_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
|
||||
USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr;
|
||||
USB_BTABLE->EP[number].USB_COUNT_RX = countrx << 10;
|
||||
#endif
|
||||
lastaddr += rxsz;
|
||||
endpoints[number].func = func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// standard IRQ handler
|
||||
void USB_IRQ(){
|
||||
uint32_t CNTR = USB->CNTR;
|
||||
USB->CNTR = 0;
|
||||
uint32_t istr = USB->ISTR;
|
||||
if(istr & USB_ISTR_RESET){
|
||||
usbON = 0;
|
||||
// Reinit registers
|
||||
CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_SUSPM;
|
||||
// Endpoint 0 - CONTROL
|
||||
// ON USB LS size of EP0 may be 8 bytes, but on FS it should be 64 bytes!
|
||||
lastaddr = LASTADDR_DEFAULT;
|
||||
// clear address, leave only enable bit
|
||||
USB->DADDR = USB_DADDR_EF;
|
||||
//USB->ISTR = ~(USB_ISTR_RESET); // clear all flags
|
||||
if(EP_Init(0, EP_TYPE_CONTROL, USB_EP0BUFSZ, USB_EP0BUFSZ, EP0_Handler)){
|
||||
return;
|
||||
};
|
||||
}
|
||||
if(istr & USB_ISTR_CTR){
|
||||
// EP number
|
||||
uint8_t n = istr & USB_ISTR_EPID;
|
||||
if (istr & USB_ISTR_DIR){ // OUT
|
||||
}else{ // IN
|
||||
}
|
||||
// copy received bytes amount
|
||||
endpoints[n].rx_cnt =
|
||||
#ifdef USB32
|
||||
(USB_BTABLE->EP[n].USB_ADDR_COUNT_RX >> 16) & 0x3FF;
|
||||
#else
|
||||
USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter
|
||||
#endif
|
||||
// call EP handler
|
||||
if(endpoints[n].func) endpoints[n].func();
|
||||
}
|
||||
if(istr & USB_ISTR_WKUP){ // wakeup
|
||||
#if defined STM32F0
|
||||
CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LPMODE | USB_CNTR_WKUPM);
|
||||
#elif defined STM32G0
|
||||
CNTR &= ~(USB_CNTR_SUSPEN | USB_CNTR_PDWN | USB_CNTR_WKUPM);
|
||||
#else
|
||||
CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LP_MODE | USB_CNTR_WKUPM); // clear suspend flags
|
||||
#endif
|
||||
//USB->ISTR = ~USB_ISTR_WKUP;
|
||||
}
|
||||
if(istr & USB_ISTR_SUSP){ // suspend -> still no connection, may sleep
|
||||
usbON = 0;
|
||||
#if defined STM32F0
|
||||
CNTR |= USB_CNTR_FSUSP | USB_CNTR_LPMODE | USB_CNTR_WKUPM;
|
||||
#elif defined STM32G0
|
||||
CNTR |= USB_CNTR_SUSPEN | USB_CNTR_WKUPM;
|
||||
#else
|
||||
CNTR |= USB_CNTR_FSUSP | USB_CNTR_LP_MODE | USB_CNTR_WKUPM;
|
||||
#endif
|
||||
CNTR &= ~(USB_CNTR_SUSPM);
|
||||
//USB->ISTR = ~USB_ISTR_SUSP;
|
||||
}
|
||||
USB->ISTR = 0; // clear all flags
|
||||
USB->CNTR = CNTR; // rewoke interrupts
|
||||
}
|
||||
|
||||
// here we suppose that all PIN settings done in hw_setup earlier
|
||||
void USB_setup(){
|
||||
lastaddr = LASTADDR_DEFAULT; // clear last address settings
|
||||
#if defined STM32F3
|
||||
NVIC_DisableIRQ(USB_LP_IRQn);
|
||||
// remap USB LP & Wakeup interrupts to 75 and 76 - works only on pure F303
|
||||
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // enable tacting of SYSCFG
|
||||
SYSCFG->CFGR1 |= SYSCFG_CFGR1_USB_IT_RMP;
|
||||
#elif defined STM32F1
|
||||
NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
|
||||
NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
|
||||
#elif defined STM32F0
|
||||
// All is clocking from HSI48
|
||||
NVIC_DisableIRQ(USB_IRQn);
|
||||
RCC->APB1ENR |= RCC_APB1ENR_CRSEN;
|
||||
RCC->CFGR3 &= ~RCC_CFGR3_USBSW; // reset USB
|
||||
RCC->CR2 |= RCC_CR2_HSI48ON; // turn ON HSI48
|
||||
uint32_t tmout = 16000000;
|
||||
while(!(RCC->CR2 & RCC_CR2_HSI48RDY)){if(--tmout == 0) break;}
|
||||
FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
|
||||
CRS->CFGR &= ~CRS_CFGR_SYNCSRC;
|
||||
CRS->CFGR |= CRS_CFGR_SYNCSRC_1; // USB SOF selected as sync source
|
||||
CRS->CR |= CRS_CR_AUTOTRIMEN; // enable auto trim
|
||||
CRS->CR |= CRS_CR_CEN; // enable freq counter & block CRS->CFGR as read-only
|
||||
RCC->CFGR |= RCC_CFGR_SW;
|
||||
#elif defined STM32G0
|
||||
NVIC_DisableIRQ(USB_UCPD1_2_IRQn);
|
||||
PWR->CR2 |= PWR_CR2_USV; // enable USB powering
|
||||
//RCC->APBENR2 |= RCC_APBENR2_SYSCFGEN; // enable tacting of SYSCFG
|
||||
// independent clocking of USB from HSI48
|
||||
RCC->CR |= RCC_CR_HSI48ON;
|
||||
uint32_t tmout = 16000000;
|
||||
while(!(RCC->CR & RCC_CR_HSI48RDY)) if(--tmout == 0){ break;}
|
||||
RCC->CCIPR2 &= ~RCC_CCIPR2_USBSEL; // select HSI48 for USB
|
||||
RCC->APBENR1 |= RCC_APBENR1_CRSEN; // CRS clocking
|
||||
CRS->CFGR = (31LL << CRS_CFGR_FELIM_Pos) | // tolerance (usually 31)
|
||||
(48000LL / 1LL - 1LL) << CRS_CFGR_RELOAD_Pos | // 48MHz / 1kHZ (SOF)
|
||||
CRS_CFGR_SYNCSRC_1; // USB SOF as sync source (0x2)
|
||||
CRS->CR |= CRS_CR_AUTOTRIMEN | CRS_CR_CEN; // Enable autotrim and turn on Clock Recovery System
|
||||
RCC->APBENR1 |= RCC_APBENR1_USBEN;
|
||||
#endif
|
||||
#ifndef STM32G0
|
||||
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
|
||||
USB->CNTR = USB_CNTR_FRES; // Force USB Reset
|
||||
USB->BTABLE = 0;
|
||||
#else
|
||||
USB->CNTR = USB_CNTR_USBRST;
|
||||
#endif
|
||||
for(uint32_t ctr = 0; ctr < 72000; ++ctr) nop(); // wait >1ms
|
||||
USB->CNTR = USB_CNTR_RESETM; // allow only reset interrupts
|
||||
USB->DADDR = 0;
|
||||
USB->ISTR = 0;
|
||||
#if defined STM32F3
|
||||
NVIC_EnableIRQ(USB_LP_IRQn);
|
||||
#elif defined STM32F1
|
||||
NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
|
||||
#elif defined STM32F0
|
||||
USB->BCDR |= USB_BCDR_DPPU;
|
||||
NVIC_EnableIRQ(USB_IRQn);
|
||||
#elif defined STM32G0
|
||||
USB->BCDR |= USB_BCDR_DPPU; // turn ON DP pullup
|
||||
NVIC_EnableIRQ(USB_UCPD1_2_IRQn);
|
||||
#endif
|
||||
setup_interfaces();
|
||||
}
|
||||
|
||||
|
||||
#if defined STM32F3
|
||||
void usb_lp_isr() __attribute__ ((alias ("USB_IRQ")));
|
||||
#elif defined STM32F1
|
||||
void usb_lp_can_rx0_isr() __attribute__ ((alias ("USB_IRQ")));
|
||||
#elif defined STM32F0
|
||||
void usb_isr() __attribute__ ((alias ("USB_IRQ")));
|
||||
#elif defined STM32G0
|
||||
void usb_ucpd1_2_isr() __attribute__ ((alias ("USB_IRQ")));
|
||||
#endif
|
||||
352
F1:F103/AS3935-lightning/usb_lib.h
Normal file
352
F1:F103/AS3935-lightning/usb_lib.h
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#ifndef _U_
|
||||
#define _U_ __attribute__((unused))
|
||||
#endif
|
||||
|
||||
/******************************************************************
|
||||
* Hardware registers etc *
|
||||
*****************************************************************/
|
||||
#if defined STM32F0
|
||||
#include <stm32f0.h>
|
||||
#elif defined STM32F1
|
||||
#include <stm32f1.h>
|
||||
// there's no this define in standard header
|
||||
#define USB_BASE ((uint32_t)0x40005C00)
|
||||
#elif defined STM32F3
|
||||
#include <stm32f3.h>
|
||||
#elif defined STM32G0
|
||||
#include <stm32g0.h>
|
||||
#endif
|
||||
|
||||
// max endpoints number
|
||||
#define STM32ENDPOINTS 8
|
||||
/**
|
||||
* Buffers size definition
|
||||
**/
|
||||
|
||||
// F0 - USB2_16; F1 - USB1_16; F3 - 1/2 depending on series; G0 - USB32
|
||||
#if !defined USB1_16 && !defined USB2_16 && !defined USB32
|
||||
#if defined STM32F0
|
||||
#define USB2_16
|
||||
#elif defined STM32F1
|
||||
#define USB1_16
|
||||
#elif defined STM32G0
|
||||
#define USB32
|
||||
#else
|
||||
#error "Can't determine USB1_16/USB2_16/USB32, define by hands"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// BTABLE_SIZE FOR STM32F3:
|
||||
// In STM32F303/302xB/C, 512 bytes SRAM is not shared with CAN.
|
||||
// In STM32F302x6/x8 and STM32F30xxD/E, 726 bytes dedicated SRAM and 256 bytes shared SRAM with CAN i.e.
|
||||
// 1Kbytes dedicated SRAM in case CAN is disabled.
|
||||
// remember, that USB_BTABLE_SIZE will be divided by ACCESSZ, so don't divide it twice for 32-bit addressing
|
||||
|
||||
#ifdef NOCAN
|
||||
#if defined STM32F0
|
||||
#define USB_BTABLE_SIZE 1024
|
||||
#elif defined STM32F3
|
||||
#define USB_BTABLE_SIZE 1024
|
||||
//#warning "Please, check real buffer size due to docs"
|
||||
#else
|
||||
#error "define STM32F0 or STM32F3"
|
||||
#endif
|
||||
#else // !NOCAN: F0/F3 with CAN or F1 (can't simultaneously run CAN and USB)
|
||||
#if defined STM32F0
|
||||
#define USB_BTABLE_SIZE 768
|
||||
#elif defined STM32F3
|
||||
#define USB_BTABLE_SIZE 768
|
||||
#elif defined STM32G0
|
||||
#define USB_BTABLE_SIZE 2048
|
||||
//#warning "Please, check real buffer size due to docs"
|
||||
#else // STM32F103: 1024 bytes but with 32-bit addressing
|
||||
#define USB_BTABLE_SIZE 1024
|
||||
#endif
|
||||
#endif // NOCAN
|
||||
|
||||
// first 64 bytes of USB_BTABLE are registers!
|
||||
#ifndef STM32G0
|
||||
#define USB_BTABLE_BASE 0x40006000
|
||||
#else
|
||||
#define USB_BTABLE_BASE 0x40009800
|
||||
#endif
|
||||
#define USB ((USB_TypeDef *) USB_BASE)
|
||||
|
||||
#ifdef USB_BTABLE
|
||||
#undef USB_BTABLE
|
||||
#endif
|
||||
#define USB_BTABLE ((USB_BtableDef *)(USB_BTABLE_BASE))
|
||||
#define USB_ISTR_EPID 0x0000000F
|
||||
#define USB_FNR_LSOF_0 0x00000800
|
||||
#define USB_FNR_lSOF_1 0x00001000
|
||||
#define USB_LPMCSR_BESL_0 0x00000010
|
||||
#define USB_LPMCSR_BESL_1 0x00000020
|
||||
#define USB_LPMCSR_BESL_2 0x00000040
|
||||
#define USB_LPMCSR_BESL_3 0x00000080
|
||||
#define USB_EPnR_CTR_RX 0x00008000
|
||||
#define USB_EPnR_DTOG_RX 0x00004000
|
||||
#define USB_EPnR_STAT_RX 0x00003000
|
||||
#define USB_EPnR_STAT_RX_0 0x00001000
|
||||
#define USB_EPnR_STAT_RX_1 0x00002000
|
||||
#define USB_EPnR_SETUP 0x00000800
|
||||
#define USB_EPnR_EP_TYPE 0x00000600
|
||||
#define USB_EPnR_EP_TYPE_0 0x00000200
|
||||
#define USB_EPnR_EP_TYPE_1 0x00000400
|
||||
#define USB_EPnR_EP_KIND 0x00000100
|
||||
#define USB_EPnR_CTR_TX 0x00000080
|
||||
#define USB_EPnR_DTOG_TX 0x00000040
|
||||
#define USB_EPnR_STAT_TX 0x00000030
|
||||
#define USB_EPnR_STAT_TX_0 0x00000010
|
||||
#define USB_EPnR_STAT_TX_1 0x00000020
|
||||
#define USB_EPnR_EA 0x0000000F
|
||||
#define USB_COUNTn_RX_BLSIZE 0x00008000
|
||||
#define USB_COUNTn_NUM_BLOCK 0x00007C00
|
||||
#define USB_COUNTn_RX 0x0000003F
|
||||
|
||||
#define USB_TypeDef USB_TypeDef_custom
|
||||
|
||||
typedef struct {
|
||||
__IO uint32_t EPnR[STM32ENDPOINTS];
|
||||
__IO uint32_t RESERVED[STM32ENDPOINTS];
|
||||
__IO uint32_t CNTR;
|
||||
__IO uint32_t ISTR;
|
||||
__IO uint32_t FNR;
|
||||
__IO uint32_t DADDR;
|
||||
#ifndef USB32
|
||||
__IO uint32_t BTABLE;
|
||||
#else
|
||||
__IO uint32_t RESERVED1; // there's no BTABLE register in STM32G0
|
||||
#endif
|
||||
#if defined STM32F0 || defined USB32
|
||||
__IO uint32_t LPMCSR;
|
||||
__IO uint32_t BCDR;
|
||||
#endif
|
||||
} USB_TypeDef;
|
||||
|
||||
// F303 D/E have 2x16 access scheme
|
||||
typedef struct{
|
||||
#if defined USB2_16
|
||||
__IO uint16_t USB_ADDR_TX;
|
||||
__IO uint16_t USB_COUNT_TX;
|
||||
__IO uint16_t USB_ADDR_RX;
|
||||
__IO uint16_t USB_COUNT_RX;
|
||||
#define ACCESSZ (1)
|
||||
#elif defined USB1_16
|
||||
__IO uint32_t USB_ADDR_TX;
|
||||
__IO uint32_t USB_COUNT_TX;
|
||||
__IO uint32_t USB_ADDR_RX;
|
||||
__IO uint32_t USB_COUNT_RX;
|
||||
#define ACCESSZ (2)
|
||||
#elif defined USB32
|
||||
// 32-bit registers: addr & count in one!
|
||||
__IO uint32_t USB_ADDR_COUNT_TX;
|
||||
__IO uint32_t USB_ADDR_COUNT_RX;
|
||||
#define ACCESSZ (1)
|
||||
#else
|
||||
#error "Define USB1_16 (16 bits over 32bit register), USB2_16 (16 bits over 16 bit register) or USB32 (32 bist over 32 bit register)"
|
||||
#endif
|
||||
} USB_EPDATA_TypeDef;
|
||||
|
||||
|
||||
typedef struct{
|
||||
__IO USB_EPDATA_TypeDef EP[STM32ENDPOINTS];
|
||||
} USB_BtableDef;
|
||||
|
||||
#define EP0DATABUF_SIZE (64)
|
||||
#define LASTADDR_DEFAULT (STM32ENDPOINTS * 8)
|
||||
|
||||
/******************************************************************
|
||||
* Defines from usb.h *
|
||||
*****************************************************************/
|
||||
|
||||
/*
|
||||
* Device and/or Interface Class codes
|
||||
*/
|
||||
#define USB_CLASS_PER_INTERFACE 0
|
||||
#define USB_CLASS_AUDIO 1
|
||||
#define USB_CLASS_COMM 2
|
||||
#define USB_CLASS_HID 3
|
||||
#define USB_CLASS_PRINTER 7
|
||||
#define USB_CLASS_PTP 6
|
||||
#define USB_CLASS_MASS_STORAGE 8
|
||||
#define USB_CLASS_HUB 9
|
||||
#define USB_CLASS_DATA 10
|
||||
#define USB_CLASS_MISC 0xef
|
||||
#define USB_CLASS_VENDOR_SPEC 0xff
|
||||
|
||||
/*
|
||||
* Descriptor types
|
||||
*/
|
||||
#define USB_DT_DEVICE 0x01
|
||||
#define USB_DT_CONFIG 0x02
|
||||
#define USB_DT_STRING 0x03
|
||||
#define USB_DT_INTERFACE 0x04
|
||||
#define USB_DT_ENDPOINT 0x05
|
||||
#define USB_DT_QUALIFIER 0x06
|
||||
#define USB_DT_IAD 0x0B
|
||||
|
||||
#define USB_DT_HID 0x21
|
||||
#define USB_DT_REPORT 0x22
|
||||
#define USB_DT_PHYSICAL 0x23
|
||||
#define USB_DT_CS_INTERFACE 0x24
|
||||
#define USB_DT_HUB 0x29
|
||||
|
||||
/*
|
||||
* Descriptor sizes per descriptor type
|
||||
*/
|
||||
#define USB_DT_DEVICE_SIZE 18
|
||||
#define USB_DT_CONFIG_SIZE 9
|
||||
#define USB_DT_INTERFACE_SIZE 9
|
||||
#define USB_DT_HID_SIZE 9
|
||||
#define USB_DT_ENDPOINT_SIZE 7
|
||||
#define USB_DT_QUALIFIER_SIZE 10
|
||||
#define USB_DT_CS_INTERFACE_SIZE 5
|
||||
#define USB_DT_IAD_SIZE 8
|
||||
|
||||
|
||||
// bmRequestType & 0x80 == dev2host (1) or host2dev (0)
|
||||
// recipient: bmRequestType & 0x1f
|
||||
#define REQUEST_RECIPIENT(b) (b & 0x1f)
|
||||
#define REQ_RECIPIENT_DEVICE 0
|
||||
#define REQ_RECIPIENT_INTERFACE 1
|
||||
#define REQ_RECIPIENT_ENDPOINT 2
|
||||
#define REQ_RECIPIENT_OTHER 3
|
||||
// type: [bmRequestType & 0x60 >> 5]
|
||||
#define REQUEST_TYPE(b) ((b&0x60)>>5)
|
||||
#define REQ_TYPE_STANDARD 0
|
||||
#define REQ_TYPE_CLASS 1
|
||||
#define REQ_TYPE_VENDOR 2
|
||||
#define REQ_TYPE_RESERVED 3
|
||||
|
||||
|
||||
//#define VENDOR_REQUEST 0x01
|
||||
|
||||
// standard device requests
|
||||
#define GET_STATUS 0x00
|
||||
#define CLEAR_FEATURE 0x01
|
||||
#define SET_FEATURE 0x03
|
||||
#define SET_ADDRESS 0x05
|
||||
#define GET_DESCRIPTOR 0x06
|
||||
#define SET_DESCRIPTOR 0x07
|
||||
#define GET_CONFIGURATION 0x08
|
||||
#define SET_CONFIGURATION 0x09
|
||||
// and some standard interface requests
|
||||
#define GET_INTERFACE 0x0A
|
||||
#define SET_INTERFACE 0x0B
|
||||
// and some standard endpoint requests
|
||||
#define SYNC_FRAME 0x0C
|
||||
|
||||
// Types of descriptors
|
||||
#define DEVICE_DESCRIPTOR 0x01
|
||||
#define CONFIGURATION_DESCRIPTOR 0x02
|
||||
#define STRING_DESCRIPTOR 0x03
|
||||
#define DEVICE_QUALIFIER_DESCRIPTOR 0x06
|
||||
#define DEBUG_DESCRIPTOR 0x0a
|
||||
#define HID_REPORT_DESCRIPTOR 0x22
|
||||
|
||||
// EP types for EP_init
|
||||
#define EP_TYPE_BULK 0x00
|
||||
#define EP_TYPE_CONTROL 0x01
|
||||
#define EP_TYPE_ISO 0x02
|
||||
#define EP_TYPE_INTERRUPT 0x03
|
||||
|
||||
// EP types for descriptors
|
||||
#define USB_BM_ATTR_CONTROL 0x00
|
||||
#define USB_BM_ATTR_ISO 0x01
|
||||
#define USB_BM_ATTR_BULK 0x02
|
||||
#define USB_BM_ATTR_INTERRUPT 0x03
|
||||
|
||||
|
||||
/******************************************************************
|
||||
* Other stuff *
|
||||
*****************************************************************/
|
||||
|
||||
#define RX_FLAG(epstat) (epstat & USB_EPnR_CTR_RX)
|
||||
#define TX_FLAG(epstat) (epstat & USB_EPnR_CTR_TX)
|
||||
#define SETUP_FLAG(epstat) (epstat & USB_EPnR_SETUP)
|
||||
|
||||
// EPnR bits manipulation
|
||||
#define KEEP_DTOG_STAT(EPnR) (EPnR & ~(USB_EPnR_STAT_RX|USB_EPnR_STAT_TX|USB_EPnR_DTOG_RX|USB_EPnR_DTOG_TX))
|
||||
#define KEEP_DTOG(EPnR) (EPnR & ~(USB_EPnR_DTOG_RX|USB_EPnR_DTOG_TX))
|
||||
|
||||
#define LANG_US (uint16_t)0x0409
|
||||
|
||||
#define _USB_STRING_(name, str) \
|
||||
static const struct name \
|
||||
{ \
|
||||
uint8_t bLength; \
|
||||
uint8_t bDescriptorType; \
|
||||
uint16_t bString[(sizeof(str) - 2) / 2]; \
|
||||
\
|
||||
} \
|
||||
name = {sizeof(name), 0x03, str}
|
||||
|
||||
#define _USB_LANG_ID_(name, lng_id) \
|
||||
static const struct name \
|
||||
{ \
|
||||
uint8_t bLength; \
|
||||
uint8_t bDescriptorType; \
|
||||
uint16_t bString; \
|
||||
\
|
||||
} \
|
||||
name = {0x04, 0x03, lng_id}
|
||||
|
||||
// EP0 configuration packet
|
||||
typedef struct {
|
||||
uint8_t bmRequestType;
|
||||
uint8_t bRequest;
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
} config_pack_t;
|
||||
|
||||
// endpoints state
|
||||
typedef struct{
|
||||
#ifdef USB32
|
||||
uint32_t *tx_buf; // transmission buffer address
|
||||
#else
|
||||
uint16_t *tx_buf; // transmission buffer address
|
||||
#endif
|
||||
uint16_t txbufsz; // transmission buffer size
|
||||
#ifdef USB32
|
||||
uint32_t *rx_buf; // reception buffer address
|
||||
#else
|
||||
uint8_t *rx_buf; // reception buffer address
|
||||
#endif
|
||||
void (*func)(); // endpoint action function
|
||||
unsigned rx_cnt : 10; // received data counter
|
||||
} ep_t;
|
||||
|
||||
extern volatile uint8_t usbON;
|
||||
|
||||
void USB_setup();
|
||||
int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)());
|
||||
void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size);
|
||||
void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size);
|
||||
int EP_Read(uint8_t number, uint8_t *buf);
|
||||
|
||||
// could be [re]defined in usb_dev.c
|
||||
extern void usb_class_request(config_pack_t *packet, uint8_t *data, uint16_t datalen);
|
||||
extern void usb_vendor_request(config_pack_t *packet, uint8_t *data, uint16_t datalen);
|
||||
extern void set_configuration();
|
||||
2
F1:F103/AS3935-lightning/version.inc
Normal file
2
F1:F103/AS3935-lightning/version.inc
Normal file
@@ -0,0 +1,2 @@
|
||||
#define BUILD_NUMBER "43"
|
||||
#define BUILD_DATE "2026-04-14"
|
||||
@@ -6,4 +6,4 @@ LDSCRIPT ?= stm32f103xB.ld
|
||||
DEFINES := -DSTM32F10X_MD
|
||||
|
||||
include ../makefile.f1
|
||||
include ../makefile.stm32
|
||||
include ../../makefile.stm32
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 18.0.2, 2026-02-13T20:51:37. -->
|
||||
<!-- Written by QtCreator 19.0.0, 2026-03-23T23:04:02. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
@@ -154,6 +154,7 @@
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
|
||||
<value type="QList<int>" key="Analyzer.Valgrind.VisibleErrorKinds"></value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
@@ -189,6 +190,7 @@
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
|
||||
<value type="QList<int>" key="Analyzer.Valgrind.VisibleErrorKinds"></value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
|
||||
@@ -47,8 +47,8 @@ typedef struct{
|
||||
typedef struct __attribute__((packed, aligned(4))){
|
||||
uint16_t userconf_sz; // "magick number"
|
||||
uint16_t send232_interval; // interval (ms) of sending data to SSII over RS-232 (or 0 - not to send)
|
||||
uint16_t iInterface[bTotNumEndpoints][MAX_IINTERFACE_SZ]; // hryunikod!
|
||||
uint8_t iIlengths[bTotNumEndpoints];
|
||||
uint16_t iInterface[InterfacesAmount][MAX_IINTERFACE_SZ]; // hryunikod!
|
||||
uint8_t iIlengths[InterfacesAmount];
|
||||
uint8_t encbits; // encoder bits: 26 or 32
|
||||
uint8_t encbufsz; // encoder buffer size (up to ENCODER_BUFSZ_MAX)
|
||||
uint8_t minzeros; // min/max zeros in preamble when searching start of record
|
||||
|
||||
@@ -116,7 +116,7 @@ static void proc_enc(uint8_t idx){
|
||||
if(CDCready[I_CMD] && the_conf.flags.debug){
|
||||
CMDWR("Err, restart SPI "); USB_putbyte(I_CMD, '1'+idx); CMDn();
|
||||
}
|
||||
spi_start_enc(idx); // restart measurement
|
||||
//spi_start_enc(idx); // restart measurement
|
||||
}
|
||||
if(the_conf.flags.monit) monitT[idx] = Tms;
|
||||
else if(testflag) spi_start_enc(idx);
|
||||
@@ -124,7 +124,7 @@ static void proc_enc(uint8_t idx){
|
||||
|
||||
int main(){
|
||||
uint32_t lastT = 0, usartT = 0;
|
||||
uint8_t oldCDCready[bTotNumEndpoints] = {0};
|
||||
uint8_t oldCDCready[InterfacesAmount] = {0};
|
||||
StartHSE();
|
||||
flashstorage_init();
|
||||
hw_setup();
|
||||
@@ -148,7 +148,7 @@ int main(){
|
||||
else if(l) parse_cmd(inbuff);
|
||||
// check if interface connected/disconnected
|
||||
// (we CAN'T do much debug output in interrupt functions like linecoding_handler etc, so do it here)
|
||||
for(int i = 1; i < bTotNumEndpoints; ++i){
|
||||
for(int i = 1; i < InterfacesAmount; ++i){
|
||||
if(oldCDCready[i] != CDCready[i]){
|
||||
CMDWR("Interface ");
|
||||
CMDWR(u2str(i));
|
||||
|
||||
@@ -133,7 +133,7 @@ static errcode_e sendenc(cmd_e idx, char *par){
|
||||
}
|
||||
|
||||
static errcode_e setiface(cmd_e idx, char *par){
|
||||
if(idx < C_setiface1 || idx >= C_setiface1 + bTotNumEndpoints) return ERR_BADCMD;
|
||||
if(idx < C_setiface1 || idx >= C_setiface1 + InterfacesAmount) return ERR_BADCMD;
|
||||
idx -= C_setiface1; // now it is an index of iIlengths
|
||||
if(par && *par){
|
||||
int l = strlen(par);
|
||||
@@ -364,7 +364,7 @@ static errcode_e dumpconf(cmd_e _U_ idx, char _U_ *par){
|
||||
CMDWR("userconf_sz="); CMDWR(u2str(the_conf.userconf_sz));
|
||||
CMDWR("\ncurrentconfidx="); CMDWR(i2str(currentconfidx));
|
||||
CMDn();
|
||||
for(int i = 0; i < bTotNumEndpoints; ++i)
|
||||
for(int i = 0; i < InterfacesAmount; ++i)
|
||||
setiface(C_setiface1 + i, NULL);
|
||||
setboolpar(C_autom, NULL);
|
||||
setuintpar(C_amperiod, NULL);
|
||||
|
||||
@@ -15,9 +15,12 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h> // memcpy
|
||||
#include <string.h>
|
||||
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#define CHK(b) do{if(!b) return -1;}while(0)
|
||||
|
||||
static int datalen(ringbuffer *b){
|
||||
if(b->tail >= b->head) return (b->tail - b->head);
|
||||
else return (b->length - b->head + b->tail);
|
||||
@@ -25,6 +28,8 @@ static int datalen(ringbuffer *b){
|
||||
|
||||
// stored data length
|
||||
int RB_datalen(ringbuffer *b){
|
||||
CHK(b);
|
||||
if(0 == datalen(b)) return 0; // don't block for empty RO operations
|
||||
if(b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int l = datalen(b);
|
||||
@@ -52,17 +57,13 @@ static int hasbyte(ringbuffer *b, uint8_t byte){
|
||||
* @return index if found, -1 if none or busy
|
||||
*/
|
||||
int RB_hasbyte(ringbuffer *b, uint8_t byte){
|
||||
CHK(b);
|
||||
if(b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int ret = hasbyte(b, byte);
|
||||
b->busy = 0;
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
// poor memcpy
|
||||
static void mcpy(uint8_t *targ, const uint8_t *src, int l){
|
||||
while(l--) *targ++ = *src++;
|
||||
}*/
|
||||
|
||||
// increment head or tail
|
||||
TRUE_INLINE void incr(ringbuffer *b, volatile int *what, int n){
|
||||
@@ -77,10 +78,8 @@ static int read(ringbuffer *b, uint8_t *s, int len){
|
||||
int _1st = b->length - b->head;
|
||||
if(_1st > l) _1st = l;
|
||||
if(_1st > len) _1st = len;
|
||||
//mcpy(s, b->data + b->head, _1st);
|
||||
memcpy(s, b->data + b->head, _1st);
|
||||
if(_1st < len && l > _1st){
|
||||
//mcpy(s+_1st, b->data, l - _1st);
|
||||
memcpy(s+_1st, b->data, l - _1st);
|
||||
incr(b, &b->head, l);
|
||||
return l;
|
||||
@@ -97,6 +96,9 @@ static int read(ringbuffer *b, uint8_t *s, int len){
|
||||
* @return bytes read or -1 if busy
|
||||
*/
|
||||
int RB_read(ringbuffer *b, uint8_t *s, int len){
|
||||
CHK(b);
|
||||
if(!s || len < 1) return -1;
|
||||
if(0 == datalen(b)) return 0;
|
||||
if(b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int r = read(b, s, len);
|
||||
@@ -104,13 +106,20 @@ int RB_read(ringbuffer *b, uint8_t *s, int len){
|
||||
return r;
|
||||
}
|
||||
|
||||
static int readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
|
||||
// length of data from current position to `byte` (including byte)
|
||||
static int lento(ringbuffer *b, uint8_t byte){
|
||||
int idx = hasbyte(b, byte);
|
||||
if(idx < 0) return 0;
|
||||
int partlen = idx + 1 - b->head;
|
||||
// now calculate length of new data portion
|
||||
if(idx < b->head) partlen += b->length;
|
||||
if(partlen > len) return -read(b, s, len);
|
||||
return partlen;
|
||||
}
|
||||
|
||||
static int readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
|
||||
int partlen = lento(b, byte);
|
||||
if(!partlen) return 0;
|
||||
if(partlen > len) return -1;
|
||||
return read(b, s, partlen);
|
||||
}
|
||||
|
||||
@@ -118,27 +127,45 @@ static int readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
|
||||
* @brief RB_readto fill array `s` with data until byte `byte` (with it)
|
||||
* @param b - ringbuffer
|
||||
* @param byte - check byte
|
||||
* @param s - buffer to write data
|
||||
* @param len - length of `s`
|
||||
* @param s - buffer to write data or NULL to clear data
|
||||
* @param len - length of `s` or 0 to clear data
|
||||
* @return amount of bytes written (negative, if len<data in buffer or buffer is busy)
|
||||
*/
|
||||
int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
|
||||
CHK(b);
|
||||
if(!s || len < 1) return -1;
|
||||
if(0 == datalen(b)) return 0;
|
||||
if(b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int n = readto(b, byte, s, len);
|
||||
int n = 0;
|
||||
if(s && len > 0){
|
||||
n = readto(b, byte, s, len);
|
||||
}else{
|
||||
incr(b, &b->head, lento(b, byte)); // just throw data out
|
||||
}
|
||||
b->busy = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
int RB_datalento(ringbuffer *b, uint8_t byte){
|
||||
CHK(b);
|
||||
if(0 == datalen(b)) return 0;
|
||||
if(b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int n = lento(b, byte);
|
||||
b->busy = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
// if l < rest of buffer, truncate and return actually written bytes
|
||||
static int write(ringbuffer *b, const uint8_t *str, int l){
|
||||
int r = b->length - 1 - datalen(b); // rest length
|
||||
if(l > r || !l) return 0;
|
||||
if(r < 1) return 0;
|
||||
if(l > r) l = r;
|
||||
int _1st = b->length - b->tail;
|
||||
if(_1st > l) _1st = l;
|
||||
//mcpy(b->data + b->tail, str, _1st);
|
||||
memcpy(b->data + b->tail, str, _1st);
|
||||
if(_1st < l){ // add another piece from start
|
||||
//mcpy(b->data, str+_1st, l-_1st);
|
||||
memcpy(b->data, str+_1st, l-_1st);
|
||||
}
|
||||
incr(b, &b->tail, l);
|
||||
@@ -153,6 +180,9 @@ static int write(ringbuffer *b, const uint8_t *str, int l){
|
||||
* @return amount of bytes written or -1 if busy
|
||||
*/
|
||||
int RB_write(ringbuffer *b, const uint8_t *str, int l){
|
||||
CHK(b);
|
||||
if(!str || l < 1) return -1;
|
||||
if(b->length - datalen(b) < 2) return 0;
|
||||
if(b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int w = write(b, str, l);
|
||||
@@ -162,10 +192,12 @@ int RB_write(ringbuffer *b, const uint8_t *str, int l){
|
||||
|
||||
// just delete all information in buffer `b`
|
||||
int RB_clearbuf(ringbuffer *b){
|
||||
CHK(b);
|
||||
if(b->busy) return -1;
|
||||
b->busy = 1;
|
||||
b->head = 0;
|
||||
b->tail = 0;
|
||||
bzero(b->data, b->length);
|
||||
b->busy = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined STM32F0
|
||||
#include <stm32f0.h>
|
||||
#elif defined STM32F1
|
||||
@@ -25,14 +27,12 @@
|
||||
#include <stm32f3.h>
|
||||
#endif
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
typedef struct{
|
||||
uint8_t *data; // data buffer
|
||||
const int length; // its length
|
||||
int head; // head index
|
||||
int tail; // tail index
|
||||
volatile atomic_int busy; // == TRUE if buffer is busy now
|
||||
volatile int busy; // == TRUE if buffer is busy now
|
||||
} ringbuffer;
|
||||
|
||||
int RB_read(ringbuffer *b, uint8_t *s, int len);
|
||||
@@ -40,4 +40,5 @@ int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len);
|
||||
int RB_hasbyte(ringbuffer *b, uint8_t byte);
|
||||
int RB_write(ringbuffer *b, const uint8_t *str, int l);
|
||||
int RB_datalen(ringbuffer *b);
|
||||
int RB_datalento(ringbuffer *b, uint8_t byte);
|
||||
int RB_clearbuf(ringbuffer *b);
|
||||
|
||||
@@ -20,11 +20,6 @@
|
||||
#include "flash.h"
|
||||
#include "usb_descr.h"
|
||||
|
||||
#undef DBG
|
||||
#define DBG(x)
|
||||
#undef DBGs
|
||||
#define DBGs(x)
|
||||
|
||||
// low/high for uint16_t
|
||||
#define L16(x) (x & 0xff)
|
||||
#define H16(x) (x >> 8)
|
||||
@@ -63,7 +58,7 @@ static const uint8_t USB_DeviceQualifierDescriptor[] = {
|
||||
0 // Reserved
|
||||
};
|
||||
|
||||
#define wTotalLength (USB_DT_CONFIG_SIZE + bTotNumEndpoints * 66)
|
||||
#define wTotalLength (USB_DT_CONFIG_SIZE + (bTotNumEndpoints * 66))
|
||||
|
||||
/*
|
||||
* _1stI - number of first interface
|
||||
@@ -166,7 +161,7 @@ static const uint8_t USB_ConfigDescriptor[] = {
|
||||
//const uint8_t HID_ReportDescriptor[];
|
||||
|
||||
_USB_LANG_ID_(LD, LANG_US);
|
||||
_USB_STRING_(SD, u"0.0.1");
|
||||
_USB_STRING_(SD, u"0.0.2");
|
||||
_USB_STRING_(MD, u"eddy@sao.ru");
|
||||
_USB_STRING_(PD, u"USB BISS-C encoders controller");
|
||||
|
||||
@@ -177,7 +172,7 @@ typedef struct{
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t bString[MAX_IINTERFACE_SZ];
|
||||
}iidescr_t;
|
||||
static iidescr_t iids[bTotNumEndpoints] = {
|
||||
static iidescr_t iids[InterfacesAmount] = {
|
||||
_USB_IIDESCR_(u"encoder_cmd"),
|
||||
_USB_IIDESCR_(u"encoder_X"),
|
||||
_USB_IIDESCR_(u"encoder_Y"),
|
||||
@@ -197,12 +192,8 @@ static void wr0(const uint8_t *buf, uint16_t size, uint16_t askedsize){
|
||||
if(askedsize < size) size = askedsize; // shortened request
|
||||
if(size < USB_EP0BUFSZ){
|
||||
EP_WriteIRQ(0, buf, size);
|
||||
DBG("short wr0");
|
||||
DBGs(uhex2str(size));
|
||||
return;
|
||||
}
|
||||
DBG("long wr0");
|
||||
DBGs(uhex2str(size));
|
||||
while(size){
|
||||
uint16_t l = size;
|
||||
if(l > USB_EP0BUFSZ) l = USB_EP0BUFSZ;
|
||||
@@ -215,7 +206,7 @@ static void wr0(const uint8_t *buf, uint16_t size, uint16_t askedsize){
|
||||
// keep DTOGs, clear CTR_RX,TX, set TX VALID, leave stat_Rx
|
||||
USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX|USB_EPnR_STAT_RX))
|
||||
^ USB_EPnR_STAT_TX;
|
||||
uint32_t ctr = 10000;
|
||||
uint32_t ctr = 1000000;
|
||||
while(--ctr && (USB->ISTR & USB_ISTR_CTR) == 0){IWDG->KR = IWDG_REFRESH;};
|
||||
if((USB->ISTR & USB_ISTR_CTR) == 0){
|
||||
return;
|
||||
@@ -229,42 +220,32 @@ void get_descriptor(config_pack_t *pack){
|
||||
uint8_t descrtype = pack->wValue >> 8,
|
||||
descridx = pack->wValue & 0xff;
|
||||
switch(descrtype){
|
||||
case DEVICE_DESCRIPTOR:
|
||||
DBG("DEVICE_DESCRIPTOR");
|
||||
wr0(USB_DeviceDescriptor, sizeof(USB_DeviceDescriptor), pack->wLength);
|
||||
break;
|
||||
case CONFIGURATION_DESCRIPTOR:
|
||||
DBG("CONFIGURATION_DESCRIPTOR");
|
||||
wr0(USB_ConfigDescriptor, sizeof(USB_ConfigDescriptor), pack->wLength);
|
||||
break;
|
||||
case STRING_DESCRIPTOR:
|
||||
DBG("STRING_DESCRIPTOR");
|
||||
if(descridx < iDESCR_AMOUNT){
|
||||
wr0((const uint8_t *)StringDescriptor[descridx], *((uint8_t*)StringDescriptor[descridx]), pack->wLength);
|
||||
DBGs(uhex2str(descridx));
|
||||
}else{
|
||||
EP_WriteIRQ(0, NULL, 0);
|
||||
DBG("Wrong index");
|
||||
DBGs(uhex2str(descridx));
|
||||
}
|
||||
break;
|
||||
case DEVICE_QUALIFIER_DESCRIPTOR:
|
||||
DBG("DEVICE_QUALIFIER_DESCRIPTOR");
|
||||
wr0(USB_DeviceQualifierDescriptor, sizeof(USB_DeviceQualifierDescriptor), pack->wLength);
|
||||
break;
|
||||
/* case HID_REPORT_DESCRIPTOR:
|
||||
wr0(HID_ReportDescriptor, sizeof(HID_ReportDescriptor), pack->wLength);
|
||||
break;*/
|
||||
default:
|
||||
break;
|
||||
case DEVICE_DESCRIPTOR:
|
||||
wr0(USB_DeviceDescriptor, sizeof(USB_DeviceDescriptor), pack->wLength);
|
||||
break;
|
||||
case CONFIGURATION_DESCRIPTOR:
|
||||
wr0(USB_ConfigDescriptor, sizeof(USB_ConfigDescriptor), pack->wLength);
|
||||
break;
|
||||
case STRING_DESCRIPTOR:
|
||||
if(descridx < iDESCR_AMOUNT){
|
||||
wr0((const uint8_t *)StringDescriptor[descridx], *((uint8_t*)StringDescriptor[descridx]), pack->wLength);
|
||||
}else{
|
||||
EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
break;
|
||||
case DEVICE_QUALIFIER_DESCRIPTOR:
|
||||
wr0(USB_DeviceQualifierDescriptor, sizeof(USB_DeviceQualifierDescriptor), pack->wLength);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// change values of iInterface by content of global config
|
||||
void setup_interfaces(){
|
||||
for(int i = 0; i < bTotNumEndpoints; ++i){
|
||||
for(int i = 0; i < InterfacesAmount; ++i){
|
||||
if(the_conf.iIlengths[i]){
|
||||
iids[i].bLength = the_conf.iIlengths[i];
|
||||
iids[i].bLength = the_conf.iIlengths[i] + 2; // +2 - for bLength and bDescriptorType
|
||||
memcpy(iids[i].bString, the_conf.iInterface[i], the_conf.iIlengths[i]);
|
||||
}
|
||||
iids[i].bDescriptorType = 0x03;
|
||||
|
||||
@@ -32,8 +32,16 @@
|
||||
#define bNumConfigurations 1
|
||||
|
||||
// amount of interfaces and endpoints (except 0) used
|
||||
#define bNumInterfaces 6
|
||||
#define bTotNumEndpoints 3
|
||||
#define InterfacesAmount 3
|
||||
// EP number of interface
|
||||
#define EPNO(i) (i + 1)
|
||||
// interface number of EPno
|
||||
#define IFNO(e) (e - 1)
|
||||
|
||||
// amount of interfaces (including virtual) except 0
|
||||
#define bNumInterfaces (2*InterfacesAmount)
|
||||
// amount of endpoints used
|
||||
#define bTotNumEndpoints (1+InterfacesAmount)
|
||||
|
||||
// powered
|
||||
#define BusPowered (1<<7)
|
||||
|
||||
@@ -38,56 +38,41 @@
|
||||
#define CONTROL_DTR 0x01
|
||||
#define CONTROL_RTS 0x02
|
||||
|
||||
// It's good to use debug here ONLY to debug into USART!
|
||||
// never try to debug USB into USB!!!
|
||||
#undef DBG
|
||||
#define DBG(x)
|
||||
#undef DBGs
|
||||
#define DBGs(x)
|
||||
|
||||
extern volatile uint32_t Tms;
|
||||
|
||||
// inbuf overflow when receiving
|
||||
static volatile uint8_t bufovrfl[bTotNumEndpoints] = {0};
|
||||
static volatile uint8_t bufovrfl[InterfacesAmount] = {0};
|
||||
|
||||
// receive buffer: hold data until chkin() call
|
||||
static uint8_t volatile rcvbuf[bTotNumEndpoints][USB_RXBUFSZ];
|
||||
static uint8_t volatile rcvbuflen[bTotNumEndpoints] = {0};
|
||||
static uint8_t volatile rcvbuf[InterfacesAmount][USB_RXBUFSZ] __attribute__((aligned(4)));
|
||||
static uint8_t volatile rcvbuflen[InterfacesAmount] = {0};
|
||||
// line coding
|
||||
#define DEFLC {115200, 0, 0, 8}
|
||||
static usb_LineCoding lineCoding[bTotNumEndpoints] = {DEFLC, DEFLC, DEFLC};
|
||||
static usb_LineCoding lineCoding[InterfacesAmount] = {DEFLC, DEFLC, DEFLC};
|
||||
// CDC configured and ready to use
|
||||
volatile uint8_t CDCready[bTotNumEndpoints] = {0};
|
||||
volatile uint8_t CDCready[InterfacesAmount] = {0};
|
||||
|
||||
// ring buffers for incoming and outgoing data
|
||||
static uint8_t obuf[bTotNumEndpoints][RBOUTSZ], ibuf[bTotNumEndpoints][RBINSZ];
|
||||
static uint8_t obuf[InterfacesAmount][RBOUTSZ], ibuf[InterfacesAmount][RBINSZ];
|
||||
#define OBUF(N) {.data = obuf[N], .length = RBOUTSZ, .head = 0, .tail = 0}
|
||||
static volatile ringbuffer rbout[bTotNumEndpoints] = {OBUF(0), OBUF(1), OBUF(2)};
|
||||
static volatile ringbuffer rbout[InterfacesAmount] = {OBUF(0), OBUF(1), OBUF(2)};
|
||||
#define IBUF(N) {.data = ibuf[N], .length = RBINSZ, .head = 0, .tail = 0}
|
||||
static volatile ringbuffer rbin[bTotNumEndpoints] = {IBUF(0), IBUF(1), IBUF(2)};
|
||||
static volatile ringbuffer rbin[InterfacesAmount] = {IBUF(0), IBUF(1), IBUF(2)};
|
||||
// last send data size (<0 if USB transfer ready)
|
||||
static volatile int lastdsz[bTotNumEndpoints] = {-1, -1, -1};
|
||||
static volatile int lastdsz[InterfacesAmount] = {-1, -1, -1};
|
||||
|
||||
// check incoming data and set ACK if need
|
||||
static void chkin(uint8_t ifno){
|
||||
static int ovrflctr = 0; // "antistall" counter
|
||||
if(bufovrfl[ifno]) return; // allow user to know that previous buffer was overflowed and cleared
|
||||
if(!rcvbuflen[ifno]) return;
|
||||
int w = RB_write((ringbuffer*)&rbin[ifno], (uint8_t*)rcvbuf[ifno], rcvbuflen[ifno]);
|
||||
if(w < 0){ // buffer busy
|
||||
DBG("Can't write into buffer: busy");
|
||||
if(w < 0){
|
||||
return;
|
||||
}else if(w == 0){ // no enough space or (WTF) incoming string larger than buffer size
|
||||
if(rcvbuflen[ifno] > rbin[ifno].length || ++ovrflctr > 9999){
|
||||
bufovrfl[ifno] = 1; // real overflow in case if ringbuffer's size less than USB buffer
|
||||
ovrflctr = 0;
|
||||
}else{
|
||||
return; // not enough space
|
||||
}
|
||||
}
|
||||
DBG("Put data into buffer");
|
||||
if(w != rcvbuflen[ifno]) bufovrfl[ifno] = 1;
|
||||
rcvbuflen[ifno] = 0;
|
||||
uint16_t status = KEEP_DTOG(USB->EPnR[1+ifno]); // don't change DTOG
|
||||
USB->EPnR[1+ifno] = (status & ~(USB_EPnR_STAT_TX|USB_EPnR_CTR_RX)) ^ USB_EPnR_STAT_RX; // prepare to get next data portion
|
||||
uint16_t status = KEEP_DTOG(USB->EPnR[EPNO(ifno)]); // don't change DTOG
|
||||
USB->EPnR[EPNO(ifno)] = (status & ~(USB_EPnR_STAT_TX|USB_EPnR_CTR_RX)) ^ USB_EPnR_STAT_RX; // prepare to get next data portion
|
||||
}
|
||||
|
||||
// called from transmit EP to send next data portion or by user - when new transmission starts
|
||||
@@ -100,97 +85,83 @@ static void send_next(uint8_t ifno){
|
||||
}
|
||||
if(buflen == 0){
|
||||
if(lastdsz[ifno] == USB_TXBUFSZ){
|
||||
EP_Write(1+ifno, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
|
||||
EP_Write(EPNO(ifno), NULL, 0); // send ZLP after 64 bits packet when nothing more to send
|
||||
lastdsz[ifno] = 0;
|
||||
}else lastdsz[ifno] = -1; // OK. User can start sending data
|
||||
return;
|
||||
}else if(buflen < 0){
|
||||
DBG("Buff busy");
|
||||
lastdsz[ifno] = -1;
|
||||
return;
|
||||
}
|
||||
DBG("Got data in buf");
|
||||
DBGs(uhex2str(buflen));
|
||||
DBGs(uhex2str(ifno));
|
||||
EP_Write(1+ifno, (uint8_t*)usbbuff, buflen);
|
||||
EP_Write(EPNO(ifno), (uint8_t*)usbbuff, buflen);
|
||||
lastdsz[ifno] = buflen;
|
||||
}
|
||||
|
||||
// data IN/OUT handler
|
||||
static void rxtx_handler(){
|
||||
uint8_t epno = (USB->ISTR & USB_ISTR_EPID), ifno = epno - 1;
|
||||
DBG("rxtx_handler");
|
||||
DBGs(uhex2str(ifno));
|
||||
if(epno > bTotNumEndpoints){
|
||||
DBG("wrong ifno");
|
||||
uint8_t epno = (USB->ISTR & USB_ISTR_EPID), ifno = IFNO(epno);
|
||||
if(ifno > InterfacesAmount-1){
|
||||
return;
|
||||
}
|
||||
uint16_t epstatus = KEEP_DTOG(USB->EPnR[epno]);
|
||||
if(RX_FLAG(epstatus)){ // receive data
|
||||
DBG("Got data");
|
||||
if(rcvbuflen[ifno]){
|
||||
bufovrfl[ifno] = 1; // lost last data
|
||||
rcvbuflen[ifno] = 0;
|
||||
DBG("OVERFULL");
|
||||
}
|
||||
rcvbuflen[ifno] = EP_Read(epno, (uint8_t*)rcvbuf[ifno]);
|
||||
DBGs(uhex2str(rcvbuflen[ifno]));
|
||||
USB->EPnR[epno] = epstatus & ~(USB_EPnR_CTR_RX | USB_EPnR_STAT_RX | USB_EPnR_STAT_TX); // keep RX in STALL state until read data
|
||||
chkin(ifno); // try to write current data into RXbuf if it's not busy
|
||||
}else{ // tx successfull
|
||||
DBG("Tx OK");
|
||||
USB->EPnR[epno] = (epstatus & ~(USB_EPnR_CTR_TX | USB_EPnR_STAT_TX)) ^ USB_EPnR_STAT_RX;
|
||||
send_next(ifno);
|
||||
}
|
||||
}
|
||||
|
||||
// weak handlers: change them somewhere else if you want to setup USART
|
||||
// SET_LINE_CODING
|
||||
void WEAK linecoding_handler(uint8_t ifno, usb_LineCoding *lc){
|
||||
lineCoding[ifno] = *lc;
|
||||
DBG("linecoding_handler");
|
||||
DBGs(uhex2str(ifno));
|
||||
}
|
||||
|
||||
static void clearbufs(uint8_t ifno){
|
||||
static void clearRbuf(uint8_t ifno){
|
||||
uint32_t T0 = Tms;
|
||||
while(Tms - T0 < 10){ // wait no more than 10ms
|
||||
if(1 == RB_clearbuf((ringbuffer*)&rbin[ifno])) break;
|
||||
}
|
||||
T0 = Tms;
|
||||
}
|
||||
|
||||
static void clearTbuf(uint8_t ifno){
|
||||
uint32_t T0 = Tms;
|
||||
while(Tms - T0 < 10){
|
||||
if(1 == RB_clearbuf((ringbuffer*)&rbout[ifno])) break;
|
||||
}
|
||||
rcvbuflen[ifno] = 0;
|
||||
}
|
||||
|
||||
// SET_LINE_CODING
|
||||
void linecoding_handler(uint8_t ifno, usb_LineCoding *lc){
|
||||
lineCoding[ifno] = *lc;
|
||||
}
|
||||
|
||||
// SET_CONTROL_LINE_STATE
|
||||
void WEAK clstate_handler(uint8_t ifno, uint16_t val){
|
||||
DBG("clstate_handler");
|
||||
DBGs(uhex2str(ifno));
|
||||
DBGs(uhex2str(val));
|
||||
if(val) clearbufs(ifno); // clear buffers on connect
|
||||
void clstate_handler(uint8_t ifno, uint16_t val){
|
||||
CDCready[ifno] = val; // CONTROL_DTR | CONTROL_RTS -> interface connected; 0 -> disconnected
|
||||
lastdsz[ifno] = -1;
|
||||
if(val){
|
||||
clearRbuf(ifno);
|
||||
clearTbuf(ifno);
|
||||
EP_reset(EPNO(ifno));
|
||||
}
|
||||
}
|
||||
|
||||
// SEND_BREAK - disconnect interface and clear its buffers
|
||||
void WEAK break_handler(uint8_t ifno){
|
||||
// this is a fake handler as classic CDC ACM never receives this
|
||||
void break_handler(uint8_t ifno){
|
||||
CDCready[ifno] = 0;
|
||||
DBG("break_handler()");
|
||||
DBGs(uhex2str(ifno));
|
||||
}
|
||||
|
||||
// USB is configured: setup endpoints
|
||||
// Interface is configured: setup endpoints
|
||||
void set_configuration(){
|
||||
DBG("set_configuration()");
|
||||
for(int i = 0; i < bTotNumEndpoints; ++i){
|
||||
for(int i = 0; i < InterfacesAmount; ++i){
|
||||
IWDG->KR = IWDG_REFRESH;
|
||||
int r = EP_Init(1+i, EP_TYPE_BULK, USB_TXBUFSZ, USB_RXBUFSZ, rxtx_handler);
|
||||
int r = EP_Init(EPNO(i), EP_TYPE_BULK, USB_TXBUFSZ, USB_RXBUFSZ, rxtx_handler);
|
||||
if(r){
|
||||
DBG("Can't init EP");
|
||||
DBGs(uhex2str(i));
|
||||
DBGs(uhex2str(r));
|
||||
// OOPS, can't init EP. What to do? Cry?
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,45 +171,33 @@ void usb_class_request(config_pack_t *req, uint8_t *data, uint16_t datalen){
|
||||
uint8_t recipient = REQUEST_RECIPIENT(req->bmRequestType);
|
||||
uint8_t dev2host = (req->bmRequestType & 0x80) ? 1 : 0;
|
||||
uint8_t ifno = req->wIndex >> 1;
|
||||
if(ifno > bTotNumEndpoints-1 && ifno != 0xff){
|
||||
DBG("wrong ifno");
|
||||
if(ifno > InterfacesAmount-1){ // wrong interface number
|
||||
EP_WriteIRQ(0, NULL, 0);
|
||||
return;
|
||||
}
|
||||
DBG("usb_class_request");
|
||||
DBGs(uhex2str(req->bRequest));
|
||||
switch(recipient){
|
||||
case REQ_RECIPIENT_INTERFACE:
|
||||
switch(req->bRequest){
|
||||
case SET_LINE_CODING:
|
||||
DBG("SET_LINE_CODING");
|
||||
if(!data || !datalen) break; // wait for data
|
||||
if(datalen == sizeof(usb_LineCoding))
|
||||
linecoding_handler(ifno, (usb_LineCoding*)data);
|
||||
break;
|
||||
case GET_LINE_CODING:
|
||||
DBG("GET_LINE_CODING");
|
||||
EP_WriteIRQ(0, (uint8_t*)&lineCoding[ifno], sizeof(lineCoding));
|
||||
break;
|
||||
case SET_CONTROL_LINE_STATE:
|
||||
DBG("SET_CONTROL_LINE_STATE");
|
||||
clstate_handler(ifno, req->wValue);
|
||||
break;
|
||||
case SEND_BREAK:
|
||||
DBG("SEND_BREAK");
|
||||
break_handler(ifno);
|
||||
break;
|
||||
default:
|
||||
DBG("Wrong");
|
||||
DBGs(uhex2str(req->bRequest));
|
||||
DBGs(uhex2str(datalen));
|
||||
}
|
||||
case REQ_RECIPIENT_INTERFACE:
|
||||
switch(req->bRequest){
|
||||
case SET_LINE_CODING:
|
||||
if(!data || !datalen) break; // wait for data
|
||||
if(datalen == sizeof(usb_LineCoding))
|
||||
linecoding_handler(ifno, (usb_LineCoding*)data);
|
||||
break;
|
||||
case GET_LINE_CODING:
|
||||
EP_WriteIRQ(0, (uint8_t*)&lineCoding[ifno], sizeof(lineCoding));
|
||||
break;
|
||||
case SET_CONTROL_LINE_STATE:
|
||||
clstate_handler(ifno, req->wValue);
|
||||
break;
|
||||
case SEND_BREAK:
|
||||
break_handler(ifno);
|
||||
break;
|
||||
default: // WTF?
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DBG("Wrong");
|
||||
DBGs(uhex2str(recipient));
|
||||
DBGs(uhex2str(datalen));
|
||||
DBGs(uhex2str(req->bRequest));
|
||||
if(dev2host) EP_WriteIRQ(0, NULL, 0);
|
||||
default: // WTF?
|
||||
if(dev2host) EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
if(!dev2host) EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
@@ -248,7 +207,6 @@ int USB_sendall(uint8_t ifno){
|
||||
uint32_t T0 = Tms;
|
||||
while(lastdsz[ifno] > 0){
|
||||
if(Tms - T0 > DISCONN_TMOUT){
|
||||
break_handler(ifno);
|
||||
return FALSE;
|
||||
}
|
||||
if(!CDCready[ifno]) return FALSE;
|
||||
@@ -257,17 +215,25 @@ int USB_sendall(uint8_t ifno){
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// return amount of free space in buffer
|
||||
int USB_sendbufspace(uint8_t ifno){
|
||||
if(!CDCready[ifno]) return 0;
|
||||
return rbout[ifno].length - RB_datalen((ringbuffer*)&rbout[ifno]);
|
||||
}
|
||||
|
||||
// put `buf` into queue to send
|
||||
int USB_send(uint8_t ifno, const uint8_t *buf, int len){
|
||||
if(!buf || !CDCready[ifno] || !len) return FALSE;
|
||||
DBG("USB_send");
|
||||
if(!buf || !CDCready[ifno] || !len){
|
||||
return FALSE;
|
||||
}
|
||||
uint32_t T0 = Tms;
|
||||
while(len){
|
||||
if(Tms - T0 > DISCONN_TMOUT){
|
||||
break_handler(ifno);
|
||||
return FALSE;
|
||||
}
|
||||
if(!CDCready[ifno]) return FALSE;
|
||||
if(!CDCready[ifno]){
|
||||
return FALSE;
|
||||
}
|
||||
IWDG->KR = IWDG_REFRESH;
|
||||
int l = RB_datalen((ringbuffer*)&rbout[ifno]);
|
||||
if(l < 0) continue;
|
||||
@@ -285,7 +251,9 @@ int USB_send(uint8_t ifno, const uint8_t *buf, int len){
|
||||
if(lastdsz[ifno] < 0) send_next(ifno);
|
||||
}
|
||||
}
|
||||
if(buf[len-1] == '\n' && lastdsz[ifno] < 0) send_next(ifno);
|
||||
if(buf[len-1] == '\n' && lastdsz[ifno] < 0){
|
||||
send_next(ifno);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -295,7 +263,6 @@ int USB_putbyte(uint8_t ifno, uint8_t byte){
|
||||
uint32_t T0 = Tms;
|
||||
while((l = RB_write((ringbuffer*)&rbout[ifno], &byte, 1)) != 1){
|
||||
if(Tms - T0 > DISCONN_TMOUT){
|
||||
break_handler(ifno);
|
||||
return FALSE;
|
||||
}
|
||||
if(!CDCready[ifno]) return FALSE;
|
||||
@@ -306,7 +273,9 @@ int USB_putbyte(uint8_t ifno, uint8_t byte){
|
||||
}
|
||||
}
|
||||
// send line if got EOL
|
||||
if(byte == '\n' && lastdsz[ifno] < 0) send_next(ifno);
|
||||
if(byte == '\n' && lastdsz[ifno] < 0){
|
||||
send_next(ifno);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -317,6 +286,10 @@ int USB_sendstr(uint8_t ifno, const char *string){
|
||||
return USB_send(ifno, (const uint8_t*)string, len);
|
||||
}
|
||||
|
||||
int USB_rcvlen(uint8_t ifno){
|
||||
return RB_datalen((ringbuffer*)&rbin[ifno]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USB_receive - get binary data from receiving ring-buffer
|
||||
* @param buf (i) - buffer for received data
|
||||
@@ -324,17 +297,15 @@ int USB_sendstr(uint8_t ifno, const char *string){
|
||||
* @return amount of received bytes (negative, if overfull happened)
|
||||
*/
|
||||
int USB_receive(uint8_t ifno, uint8_t *buf, int len){
|
||||
if(!CDCready[ifno]) return 0;
|
||||
chkin(ifno); // rxtx_handler could leave last message unwritten if buffer was busy
|
||||
if(bufovrfl[ifno]){
|
||||
DBG("Buffer overflow");
|
||||
DBGs(uhex2str(ifno));
|
||||
while(1 != RB_clearbuf((ringbuffer*)&rbin[ifno])); // run watchdog in case of problems
|
||||
clearRbuf(ifno);
|
||||
bufovrfl[ifno] = 0;
|
||||
return -1;
|
||||
}
|
||||
int sz = RB_read((ringbuffer*)&rbin[ifno], buf, len);
|
||||
if(sz < 0) return 0; // buffer in writting state
|
||||
DBG("usb read");
|
||||
return sz;
|
||||
}
|
||||
|
||||
@@ -345,24 +316,22 @@ int USB_receive(uint8_t ifno, uint8_t *buf, int len){
|
||||
* @return strlen or negative value indicating overflow (if so, string won't be ends with 0 and buffer should be cleared)
|
||||
*/
|
||||
int USB_receivestr(uint8_t ifno, char *buf, int len){
|
||||
if(!CDCready[ifno]) return 0;
|
||||
chkin(ifno); // rxtx_handler could leave last message unwritten if buffer was busy
|
||||
if(bufovrfl[ifno]){
|
||||
while(1 != RB_clearbuf((ringbuffer*)&rbin[ifno]));
|
||||
clearRbuf(ifno);
|
||||
bufovrfl[ifno] = 0;
|
||||
return -1;
|
||||
}
|
||||
int l = RB_readto((ringbuffer*)&rbin[ifno], '\n', (uint8_t*)buf, len);
|
||||
if(l < 1){
|
||||
//if(rbin[ifno].length < 1 + RB_datalen((ringbuffer*)&rbin[ifno])){ // buffer is full but no '\n' found
|
||||
if(RB_datalen((ringbuffer*)&rbin[ifno]) >= len){
|
||||
CMDWRn("OVERFULL!");
|
||||
while(1 != RB_clearbuf((ringbuffer*)&rbin[ifno]));
|
||||
if((rbin[ifno].length <= RB_datalen((ringbuffer*)&rbin[ifno]) + 1) ||
|
||||
(RB_datalento((ringbuffer*)&rbin[ifno], '\n') > len - 1)){ // buffer is full but no '\n' found or string too long
|
||||
clearRbuf(ifno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if(l == 0) return 0;
|
||||
buf[l-1] = 0; // replace '\n' with strend
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,14 +40,14 @@ typedef struct {
|
||||
uint8_t bDataBits;
|
||||
} __attribute__ ((packed)) usb_LineCoding;
|
||||
|
||||
extern volatile uint8_t CDCready[bTotNumEndpoints];
|
||||
extern volatile uint8_t CDCready[InterfacesAmount];
|
||||
|
||||
void break_handler(uint8_t ifno);
|
||||
void clstate_handler(uint8_t ifno, uint16_t val);
|
||||
void linecoding_handler(uint8_t ifno, usb_LineCoding *lc);
|
||||
|
||||
// as ugly CDC have no BREAK after disconnected client in non-canonical mode, we should use timeout - more than 2ms
|
||||
#define DISCONN_TMOUT (1000)
|
||||
#define DISCONN_TMOUT (2)
|
||||
|
||||
// sizes of ringbuffers for outgoing and incoming data
|
||||
#define RBOUTSZ (512)
|
||||
@@ -69,9 +69,11 @@ void linecoding_handler(uint8_t ifno, usb_LineCoding *lc);
|
||||
#define DBGs(s)
|
||||
#endif
|
||||
|
||||
int USB_sendbufspace(uint8_t ifno);
|
||||
int USB_sendall(uint8_t ifno);
|
||||
int USB_send(uint8_t ifno, const uint8_t *buf, int len);
|
||||
int USB_putbyte(uint8_t ifno, uint8_t byte);
|
||||
int USB_sendstr(uint8_t ifno, const char *string);
|
||||
int USB_rcvlen(uint8_t ifno);
|
||||
int USB_receive(uint8_t ifno, uint8_t *buf, int len);
|
||||
int USB_receivestr(uint8_t ifno, char *buf, int len);
|
||||
|
||||
@@ -20,15 +20,10 @@
|
||||
#include "usb_descr.h"
|
||||
#include "usb_dev.h"
|
||||
|
||||
#undef DBG
|
||||
#define DBG(x)
|
||||
#undef DBGs
|
||||
#define DBGs(x)
|
||||
|
||||
static ep_t endpoints[STM32ENDPOINTS];
|
||||
|
||||
static uint16_t USB_Addr = 0;
|
||||
static uint8_t setupdatabuf[EP0DATABUF_SIZE];
|
||||
static uint8_t setupdatabuf[EP0DATABUF_SIZE] __attribute__((aligned(4)));
|
||||
static config_pack_t *setup_packet = (config_pack_t*) setupdatabuf;
|
||||
volatile uint8_t usbON = 0; // device is configured and active
|
||||
|
||||
@@ -37,20 +32,15 @@ static inline void std_d2h_req(){
|
||||
uint16_t st = 0;
|
||||
switch(setup_packet->bRequest){
|
||||
case GET_DESCRIPTOR:
|
||||
DBG("GET_DESCRIPTOR");
|
||||
get_descriptor(setup_packet);
|
||||
break;
|
||||
case GET_STATUS:
|
||||
DBG("GET_STATUS");
|
||||
EP_WriteIRQ(0, (uint8_t *)&st, 2); // send status: Bus Powered
|
||||
break;
|
||||
case GET_CONFIGURATION:
|
||||
DBG("GET_CONFIGURATION");
|
||||
EP_WriteIRQ(0, (uint8_t*)&configuration, 1);
|
||||
break;
|
||||
default:
|
||||
DBG("Wrong");
|
||||
DBGs(uhex2str(setup_packet->bRequest));
|
||||
EP_WriteIRQ(0, NULL, 0);
|
||||
break;
|
||||
}
|
||||
@@ -59,21 +49,16 @@ static inline void std_d2h_req(){
|
||||
static inline void std_h2d_req(){
|
||||
switch(setup_packet->bRequest){
|
||||
case SET_ADDRESS:
|
||||
DBG("SET_ADDRESS");
|
||||
// new address will be assigned later - after acknowlegement or request to host
|
||||
USB_Addr = setup_packet->wValue;
|
||||
DBGs(uhex2str(USB_Addr));
|
||||
break;
|
||||
case SET_CONFIGURATION:
|
||||
DBG("SET_CONFIGURATION");
|
||||
// Now device configured
|
||||
configuration = setup_packet->wValue;
|
||||
set_configuration();
|
||||
usbON = 1;
|
||||
break;
|
||||
default:
|
||||
DBG("Wrong");
|
||||
DBGs(uhex2str(setup_packet->bRequest));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -83,7 +68,6 @@ void WEAK usb_standard_request(){
|
||||
uint8_t dev2host = (setup_packet->bmRequestType & 0x80) ? 1 : 0;
|
||||
switch(recipient){
|
||||
case REQ_RECIPIENT_DEVICE:
|
||||
DBG("REQ_RECIPIENT_DEVICE");
|
||||
if(dev2host){
|
||||
std_d2h_req();
|
||||
}else{
|
||||
@@ -91,49 +75,34 @@ void WEAK usb_standard_request(){
|
||||
}
|
||||
break;
|
||||
case REQ_RECIPIENT_INTERFACE:
|
||||
DBG("REQ_RECIPIENT_INTERFACE");
|
||||
if(dev2host && setup_packet->bRequest == GET_DESCRIPTOR){
|
||||
get_descriptor(setup_packet);
|
||||
}
|
||||
break;
|
||||
case REQ_RECIPIENT_ENDPOINT:
|
||||
DBG("REQ_RECIPIENT_ENDPOINT");
|
||||
if(setup_packet->bRequest == CLEAR_FEATURE){
|
||||
}else{
|
||||
DBG("Wrong");
|
||||
}
|
||||
}else{ /* wrong */ }
|
||||
break;
|
||||
default:
|
||||
DBG("Wrong");
|
||||
DBGs(uhex2str(recipient));
|
||||
break;
|
||||
}
|
||||
if(!dev2host) EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
|
||||
void WEAK usb_class_request(config_pack_t *req, uint8_t _U_ *data, uint16_t _U_ datalen){
|
||||
DBG("REQ_TYPE_CLASS");
|
||||
switch(req->bRequest){
|
||||
case GET_INTERFACE:
|
||||
DBG("GI");
|
||||
break;
|
||||
case SET_CONFIGURATION: // set featuring by req->wValue
|
||||
DBG("SC");
|
||||
break;
|
||||
default:
|
||||
DBG("Wrong");
|
||||
DBGs(uhex2str(req->bmRequestType));
|
||||
DBGs(uhex2str(req->bRequest));
|
||||
DBGs(uhex2str(req->wIndex));
|
||||
DBGs(uhex2str(req->wLength));
|
||||
DBGs(uhex2str(req->wValue));
|
||||
break;
|
||||
}
|
||||
if(0 == (setup_packet->bmRequestType & 0x80)) // host2dev
|
||||
EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
|
||||
void WEAK usb_vendor_request(config_pack_t _U_ *packet, uint8_t _U_ *data, uint16_t _U_ datalen){
|
||||
DBG("vendor");
|
||||
if(0 == (setup_packet->bmRequestType & 0x80)) // host2dev
|
||||
EP_WriteIRQ(0, NULL, 0);
|
||||
}
|
||||
@@ -149,18 +118,16 @@ bmRequestType: 76543210
|
||||
*/
|
||||
static void EP0_Handler(){
|
||||
uint8_t ep0dbuflen = 0;
|
||||
uint8_t ep0databuf[EP0DATABUF_SIZE];
|
||||
uint8_t ep0databuf[EP0DATABUF_SIZE] __attribute__((aligned(4)));
|
||||
uint16_t epstatus = KEEP_DTOG(USB->EPnR[0]); // EP0R on input -> return this value after modifications
|
||||
int rxflag = RX_FLAG(epstatus);
|
||||
if(rxflag){ DBG("EP0_Handler"); }
|
||||
// check direction
|
||||
if(USB->ISTR & USB_ISTR_DIR){ // OUT interrupt - receive data, CTR_RX==1 (if CTR_TX == 1 - two pending transactions: receive following by transmit)
|
||||
if(epstatus & USB_EPnR_SETUP){ // setup packet -> copy data to conf_pack
|
||||
DBG("USB_EPnR_SETUP");
|
||||
EP_Read(0, setupdatabuf);
|
||||
// interrupt handler will be called later
|
||||
}else if(epstatus & USB_EPnR_CTR_RX){ // data packet -> push received data to ep0databuf
|
||||
if(endpoints[0].rx_cnt){ DBG("data"); DBGs(uhex2str(endpoints[0].rx_cnt));}
|
||||
//if(endpoints[0].rx_cnt){ }
|
||||
ep0dbuflen = EP_Read(0, ep0databuf);
|
||||
}
|
||||
}
|
||||
@@ -169,23 +136,16 @@ static void EP0_Handler(){
|
||||
switch(reqtype){
|
||||
case REQ_TYPE_STANDARD:
|
||||
if(SETUP_FLAG(epstatus)){
|
||||
DBG("REQ_TYPE_STANDARD");
|
||||
usb_standard_request();
|
||||
}else{
|
||||
DBG("REQ_TYPE_STANDARD without SETUP_FLAG");
|
||||
}
|
||||
}else{ }
|
||||
break;
|
||||
case REQ_TYPE_CLASS:
|
||||
DBG("REQ_TYPE_CLASS");
|
||||
usb_class_request(setup_packet, ep0databuf, ep0dbuflen);
|
||||
break;
|
||||
case REQ_TYPE_VENDOR:
|
||||
DBG("REQ_TYPE_VENDOR");
|
||||
usb_vendor_request(setup_packet, ep0databuf, ep0dbuflen);
|
||||
break;
|
||||
default:
|
||||
DBG("Wrong");
|
||||
DBGs(uhex2str(reqtype));
|
||||
EP_WriteIRQ(0, NULL, 0);
|
||||
break;
|
||||
}
|
||||
@@ -195,8 +155,6 @@ static void EP0_Handler(){
|
||||
if ((USB->DADDR & USB_DADDR_ADD) != USB_Addr){
|
||||
USB->DADDR = USB_DADDR_EF | USB_Addr;
|
||||
usbON = 0;
|
||||
DBG("Enum");
|
||||
DBGs(uhex2str(USB_Addr));
|
||||
}
|
||||
}
|
||||
//epstatus = KEEP_DTOG(USB->EPnR[0]);
|
||||
@@ -214,9 +172,14 @@ static void EP0_Handler(){
|
||||
*/
|
||||
void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){
|
||||
if(size > endpoints[number].txbufsz) size = endpoints[number].txbufsz;
|
||||
#ifndef USB32
|
||||
uint16_t N2 = (size + 1) >> 1;
|
||||
// the buffer is 16-bit, so we should copy data as it would be uint16_t
|
||||
uint16_t *buf16 = (uint16_t *)buf;
|
||||
#else
|
||||
int N4 = (size + 3) >> 2;
|
||||
uint32_t *buf32 = (uint32_t *)buf;
|
||||
#endif
|
||||
#if defined USB1_16
|
||||
// very bad: what if `size` is odd?
|
||||
uint32_t *out = (uint32_t *)endpoints[number].tx_buf;
|
||||
@@ -225,13 +188,19 @@ void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){
|
||||
}
|
||||
#elif defined USB2_16
|
||||
// use memcpy instead?
|
||||
for(int i = 0; i < N2; i++){
|
||||
for(int i = 0; i < N2; ++i){
|
||||
endpoints[number].tx_buf[i] = buf16[i];
|
||||
}
|
||||
#elif defined USB32
|
||||
for(int i = 0; i < N4; ++i) endpoints[number].tx_buf[i] = buf32[i];
|
||||
#else
|
||||
#error "Define USB1_16 or USB2_16"
|
||||
#error "Define USB1_16 / USB2_16 / USB32"
|
||||
#endif
|
||||
#ifndef USB32
|
||||
USB_BTABLE->EP[number].USB_COUNT_TX = size;
|
||||
#else
|
||||
USB_BTABLE->EP[number].USB_ADDR_COUNT_TX = (USB_BTABLE->EP[number].USB_ADDR_COUNT_TX & 0xffff) | (size << 16);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,8 +235,12 @@ int EP_Read(uint8_t number, uint8_t *buf){
|
||||
// use memcpy instead?
|
||||
for(int i = 0; i < sz; ++i)
|
||||
buf[i] = endpoints[number].rx_buf[i];
|
||||
#elif defined USB32
|
||||
uint32_t *u32buf = (uint32_t*) buf;
|
||||
int N4 = (sz + 3) >> 2;
|
||||
for(int i = 0; i < N4; ++i) u32buf[i] = endpoints[number].rx_buf[i];
|
||||
#else
|
||||
#error "Define USB1_16 or USB2_16"
|
||||
#error "Define USB1_16 / USB2_16 / USB32"
|
||||
#endif
|
||||
return sz;
|
||||
}
|
||||
@@ -284,11 +257,16 @@ static uint16_t lastaddr = LASTADDR_DEFAULT;
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)(ep_t ep)){
|
||||
#ifdef STM32G0
|
||||
// in STM32G0 all buffers should be aligned by 32 bits
|
||||
if(txsz & 3) txsz = ((txsz >> 2)+1) << 2;
|
||||
if(rxsz & 3) rxsz = ((rxsz >> 2)+1) << 2;
|
||||
#endif
|
||||
if(number >= STM32ENDPOINTS) return 4; // out of configured amount
|
||||
if(txsz > USB_BTABLE_SIZE/ACCESSZ || rxsz > USB_BTABLE_SIZE/ACCESSZ) return 1; // buffer too large
|
||||
if(lastaddr + txsz + rxsz >= USB_BTABLE_SIZE/ACCESSZ) return 2; // out of btable
|
||||
USB->EPnR[number] = (type << 9) | (number & USB_EPnR_EA);
|
||||
USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX_1;
|
||||
USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX;
|
||||
if(rxsz & 1) return 3; // wrong rx buffer size
|
||||
uint16_t countrx = 0;
|
||||
if(rxsz < 64) countrx = rxsz / 2;
|
||||
@@ -296,25 +274,49 @@ int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*f
|
||||
if(rxsz & 0x1f) return 3; // should be multiple of 32
|
||||
countrx = 31 + rxsz / 32;
|
||||
}
|
||||
USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr;
|
||||
#ifdef USB32
|
||||
endpoints[number].tx_buf = (uint32_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
|
||||
#else
|
||||
endpoints[number].tx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
|
||||
#endif
|
||||
endpoints[number].txbufsz = txsz;
|
||||
lastaddr += txsz;
|
||||
#ifdef USB32
|
||||
USB_BTABLE->EP[number].USB_ADDR_COUNT_TX = (uint32_t) lastaddr;
|
||||
#else
|
||||
USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr;
|
||||
USB_BTABLE->EP[number].USB_COUNT_TX = 0;
|
||||
USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr;
|
||||
#endif
|
||||
lastaddr += txsz;
|
||||
#ifdef USB32
|
||||
endpoints[number].rx_buf = (uint32_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
|
||||
USB_BTABLE->EP[number].USB_ADDR_COUNT_RX = (uint32_t) lastaddr | countrx << 26;
|
||||
#else
|
||||
endpoints[number].rx_buf = (uint8_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
|
||||
lastaddr += rxsz;
|
||||
USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr;
|
||||
USB_BTABLE->EP[number].USB_COUNT_RX = countrx << 10;
|
||||
#endif
|
||||
lastaddr += rxsz;
|
||||
endpoints[number].func = func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// refresh EP after interface reconnected
|
||||
void EP_reset(uint8_t epno){
|
||||
if(epno >= STM32ENDPOINTS) return;
|
||||
// keep DTOGs (don't write 1 to them), clear CTR (write 0 to them)
|
||||
// and set STAT to VALID (write 1 where was 0)
|
||||
uint16_t epstatus = KEEP_DTOG(USB->EPnR[epno]);
|
||||
USB->EPnR[epno] = (epstatus & ~(USB_EPnR_CTR_TX|USB_EPnR_CTR_RX)) ^
|
||||
(USB_EPnR_STAT_RX | USB_EPnR_STAT_TX);
|
||||
USB_BTABLE->EP[epno].USB_COUNT_TX = 0;
|
||||
}
|
||||
|
||||
// standard IRQ handler
|
||||
void USB_IRQ(){
|
||||
uint32_t CNTR = USB->CNTR;
|
||||
USB->CNTR = 0;
|
||||
if(USB->ISTR & USB_ISTR_RESET){
|
||||
DBG("USB_ISTR_RESET");
|
||||
uint32_t istr = USB->ISTR;
|
||||
if(istr & USB_ISTR_RESET){
|
||||
usbON = 0;
|
||||
// Reinit registers
|
||||
CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_SUSPM;
|
||||
@@ -323,45 +325,56 @@ void USB_IRQ(){
|
||||
lastaddr = LASTADDR_DEFAULT;
|
||||
// clear address, leave only enable bit
|
||||
USB->DADDR = USB_DADDR_EF;
|
||||
USB->ISTR = ~USB_ISTR_RESET;
|
||||
//USB->ISTR = ~(USB_ISTR_RESET); // clear all flags
|
||||
if(EP_Init(0, EP_TYPE_CONTROL, USB_EP0BUFSZ, USB_EP0BUFSZ, EP0_Handler)){
|
||||
DBG("Can't init EP0");
|
||||
return;
|
||||
};
|
||||
}
|
||||
if(USB->ISTR & USB_ISTR_CTR){
|
||||
if(istr & USB_ISTR_CTR){
|
||||
// EP number
|
||||
uint8_t n = USB->ISTR & USB_ISTR_EPID;
|
||||
uint8_t n = istr & USB_ISTR_EPID;
|
||||
if (istr & USB_ISTR_DIR){ // OUT
|
||||
}else{ // IN
|
||||
}
|
||||
// copy received bytes amount
|
||||
endpoints[n].rx_cnt = USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter
|
||||
endpoints[n].rx_cnt =
|
||||
#ifdef USB32
|
||||
(USB_BTABLE->EP[n].USB_ADDR_COUNT_RX >> 16) & 0x3FF;
|
||||
#else
|
||||
USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter
|
||||
#endif
|
||||
// call EP handler
|
||||
if(endpoints[n].func) endpoints[n].func();
|
||||
}
|
||||
if(USB->ISTR & USB_ISTR_WKUP){ // wakeup
|
||||
DBG("USB_ISTR_WKUP");
|
||||
#ifndef STM32F0
|
||||
CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LP_MODE | USB_CNTR_WKUPM); // clear suspend flags
|
||||
#else
|
||||
if(istr & USB_ISTR_WKUP){ // wakeup
|
||||
#if defined STM32F0
|
||||
CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LPMODE | USB_CNTR_WKUPM);
|
||||
#endif
|
||||
USB->ISTR = ~USB_ISTR_WKUP;
|
||||
}
|
||||
if(USB->ISTR & USB_ISTR_SUSP){ // suspend -> still no connection, may sleep
|
||||
DBG("USB_ISTR_SUSP");
|
||||
usbON = 0;
|
||||
#ifndef STM32F0
|
||||
CNTR |= USB_CNTR_FSUSP | USB_CNTR_LP_MODE | USB_CNTR_WKUPM;
|
||||
#elif defined STM32G0
|
||||
CNTR &= ~(USB_CNTR_SUSPEN | USB_CNTR_PDWN | USB_CNTR_WKUPM);
|
||||
#else
|
||||
CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LP_MODE | USB_CNTR_WKUPM); // clear suspend flags
|
||||
#endif
|
||||
//USB->ISTR = ~USB_ISTR_WKUP;
|
||||
}
|
||||
if(istr & USB_ISTR_SUSP){ // suspend -> still no connection, may sleep
|
||||
usbON = 0;
|
||||
#if defined STM32F0
|
||||
CNTR |= USB_CNTR_FSUSP | USB_CNTR_LPMODE | USB_CNTR_WKUPM;
|
||||
#elif defined STM32G0
|
||||
CNTR |= USB_CNTR_SUSPEN | USB_CNTR_WKUPM;
|
||||
#else
|
||||
CNTR |= USB_CNTR_FSUSP | USB_CNTR_LP_MODE | USB_CNTR_WKUPM;
|
||||
#endif
|
||||
CNTR &= ~(USB_CNTR_SUSPM);
|
||||
USB->ISTR = ~USB_ISTR_SUSP;
|
||||
//USB->ISTR = ~USB_ISTR_SUSP;
|
||||
}
|
||||
USB->ISTR = 0; // clear all flags
|
||||
USB->CNTR = CNTR; // rewoke interrupts
|
||||
}
|
||||
|
||||
// here we suppose that all PIN settings done in hw_setup earlier
|
||||
void USB_setup(){
|
||||
lastaddr = LASTADDR_DEFAULT; // clear last address settings
|
||||
#if defined STM32F3
|
||||
NVIC_DisableIRQ(USB_LP_IRQn);
|
||||
// remap USB LP & Wakeup interrupts to 75 and 76 - works only on pure F303
|
||||
@@ -371,6 +384,7 @@ void USB_setup(){
|
||||
NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
|
||||
NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
|
||||
#elif defined STM32F0
|
||||
// All is clocking from HSI48
|
||||
NVIC_DisableIRQ(USB_IRQn);
|
||||
RCC->APB1ENR |= RCC_APB1ENR_CRSEN;
|
||||
RCC->CFGR3 &= ~RCC_CFGR3_USBSW; // reset USB
|
||||
@@ -383,16 +397,33 @@ void USB_setup(){
|
||||
CRS->CR |= CRS_CR_AUTOTRIMEN; // enable auto trim
|
||||
CRS->CR |= CRS_CR_CEN; // enable freq counter & block CRS->CFGR as read-only
|
||||
RCC->CFGR |= RCC_CFGR_SW;
|
||||
#elif defined STM32G0
|
||||
NVIC_DisableIRQ(USB_UCPD1_2_IRQn);
|
||||
PWR->CR2 |= PWR_CR2_USV; // enable USB powering
|
||||
//RCC->APBENR2 |= RCC_APBENR2_SYSCFGEN; // enable tacting of SYSCFG
|
||||
// independent clocking of USB from HSI48
|
||||
RCC->CR |= RCC_CR_HSI48ON;
|
||||
uint32_t tmout = 16000000;
|
||||
while(!(RCC->CR & RCC_CR_HSI48RDY)) if(--tmout == 0) break;
|
||||
RCC->CCIPR2 &= ~RCC_CCIPR2_USBSEL; // select HSI48 for USB
|
||||
RCC->APBENR1 |= RCC_APBENR1_CRSEN; // CRS clocking
|
||||
CRS->CFGR = (31LL << CRS_CFGR_FELIM_Pos) | // tolerance (usually 31)
|
||||
(48000LL / 1LL - 1LL) << CRS_CFGR_RELOAD_Pos | // 48MHz / 1kHZ (SOF)
|
||||
CRS_CFGR_SYNCSRC_1; // USB SOF as sync source (0x2)
|
||||
CRS->CR |= CRS_CR_AUTOTRIMEN | CRS_CR_CEN; // Enable autotrim and turn on Clock Recovery System
|
||||
RCC->APBENR1 |= RCC_APBENR1_USBEN;
|
||||
#endif
|
||||
#ifndef STM32G0
|
||||
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
|
||||
//??
|
||||
USB->CNTR = USB_CNTR_FRES; // Force USB Reset
|
||||
for(uint32_t ctr = 0; ctr < 72000; ++ctr) nop(); // wait >1ms
|
||||
USB->CNTR = 0;
|
||||
USB->BTABLE = 0;
|
||||
#else
|
||||
USB->CNTR = USB_CNTR_USBRST;
|
||||
#endif
|
||||
for(uint32_t ctr = 0; ctr < 72000; ++ctr) nop(); // wait >1ms
|
||||
USB->CNTR = USB_CNTR_RESETM; // allow only reset interrupts
|
||||
USB->DADDR = 0;
|
||||
USB->ISTR = 0;
|
||||
USB->CNTR = USB_CNTR_RESETM; // allow only reset interrupts
|
||||
#if defined STM32F3
|
||||
NVIC_EnableIRQ(USB_LP_IRQn);
|
||||
#elif defined STM32F1
|
||||
@@ -400,8 +431,11 @@ void USB_setup(){
|
||||
#elif defined STM32F0
|
||||
USB->BCDR |= USB_BCDR_DPPU;
|
||||
NVIC_EnableIRQ(USB_IRQn);
|
||||
#elif defined STM32G0
|
||||
USB->BCDR |= USB_BCDR_DPPU; // turn ON DP pullup
|
||||
NVIC_EnableIRQ(USB_UCPD1_2_IRQn);
|
||||
#endif
|
||||
setup_interfaces();
|
||||
setup_interfaces(); // refresh interfaces' names
|
||||
}
|
||||
|
||||
|
||||
@@ -411,4 +445,6 @@ void usb_lp_isr() __attribute__ ((alias ("USB_IRQ")));
|
||||
void usb_lp_can_rx0_isr() __attribute__ ((alias ("USB_IRQ")));
|
||||
#elif defined STM32F0
|
||||
void usb_isr() __attribute__ ((alias ("USB_IRQ")));
|
||||
#elif defined STM32G0
|
||||
void usb_ucpd1_2_isr() __attribute__ ((alias ("USB_IRQ")));
|
||||
#endif
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
#include <stdint.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#ifndef _U_
|
||||
#define _U_ __attribute__((unused))
|
||||
#endif
|
||||
|
||||
/******************************************************************
|
||||
* Hardware registers etc *
|
||||
*****************************************************************/
|
||||
@@ -30,6 +34,8 @@
|
||||
#define USB_BASE ((uint32_t)0x40005C00)
|
||||
#elif defined STM32F3
|
||||
#include <stm32f3.h>
|
||||
#elif defined STM32G0
|
||||
#include <stm32g0.h>
|
||||
#endif
|
||||
|
||||
// max endpoints number
|
||||
@@ -38,14 +44,16 @@
|
||||
* Buffers size definition
|
||||
**/
|
||||
|
||||
// F0 - USB2_16; F1 - USB1_16; F3 - 1/2 depending on series
|
||||
#if !defined USB1_16 && !defined USB2_16
|
||||
// F0 - USB2_16; F1 - USB1_16; F3 - 1/2 depending on series; G0 - USB32
|
||||
#if !defined USB1_16 && !defined USB2_16 && !defined USB32
|
||||
#if defined STM32F0
|
||||
#define USB2_16
|
||||
#elif defined STM32F1
|
||||
#define USB1_16
|
||||
#elif defined STM32G0
|
||||
#define USB32
|
||||
#else
|
||||
#error "Can't determine USB1_16 or USB2_16, define by hands"
|
||||
#error "Can't determine USB1_16/USB2_16/USB32, define by hands"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -59,8 +67,8 @@
|
||||
#if defined STM32F0
|
||||
#define USB_BTABLE_SIZE 1024
|
||||
#elif defined STM32F3
|
||||
#define USB_BTABLE_SIZE 512
|
||||
#warning "Please, check real buffer size due to docs"
|
||||
#define USB_BTABLE_SIZE 1024
|
||||
//#warning "Please, check real buffer size due to docs"
|
||||
#else
|
||||
#error "define STM32F0 or STM32F3"
|
||||
#endif
|
||||
@@ -68,16 +76,21 @@
|
||||
#if defined STM32F0
|
||||
#define USB_BTABLE_SIZE 768
|
||||
#elif defined STM32F3
|
||||
#define USB_BTABLE_SIZE 512
|
||||
#warning "Please, check real buffer size due to docs"
|
||||
#define USB_BTABLE_SIZE 768
|
||||
#elif defined STM32G0
|
||||
#define USB_BTABLE_SIZE 2048
|
||||
//#warning "Please, check real buffer size due to docs"
|
||||
#else // STM32F103: 1024 bytes but with 32-bit addressing
|
||||
#define USB_BTABLE_SIZE 1024
|
||||
#endif
|
||||
#endif // NOCAN
|
||||
|
||||
// first 64 bytes of USB_BTABLE are registers!
|
||||
|
||||
#ifndef STM32G0
|
||||
#define USB_BTABLE_BASE 0x40006000
|
||||
#else
|
||||
#define USB_BTABLE_BASE 0x40009800
|
||||
#endif
|
||||
#define USB ((USB_TypeDef *) USB_BASE)
|
||||
|
||||
#ifdef USB_BTABLE
|
||||
@@ -120,8 +133,12 @@ typedef struct {
|
||||
__IO uint32_t ISTR;
|
||||
__IO uint32_t FNR;
|
||||
__IO uint32_t DADDR;
|
||||
#ifndef USB32
|
||||
__IO uint32_t BTABLE;
|
||||
#ifdef STM32F0
|
||||
#else
|
||||
__IO uint32_t RESERVED1; // there's no BTABLE register in STM32G0
|
||||
#endif
|
||||
#if defined STM32F0 || defined USB32
|
||||
__IO uint32_t LPMCSR;
|
||||
__IO uint32_t BCDR;
|
||||
#endif
|
||||
@@ -135,16 +152,19 @@ typedef struct{
|
||||
__IO uint16_t USB_ADDR_RX;
|
||||
__IO uint16_t USB_COUNT_RX;
|
||||
#define ACCESSZ (1)
|
||||
#define BUFTYPE uint8_t
|
||||
#elif defined USB1_16
|
||||
__IO uint32_t USB_ADDR_TX;
|
||||
__IO uint32_t USB_COUNT_TX;
|
||||
__IO uint32_t USB_ADDR_RX;
|
||||
__IO uint32_t USB_COUNT_RX;
|
||||
#define ACCESSZ (2)
|
||||
#define BUFTYPE uint16_t
|
||||
#elif defined USB32
|
||||
// 32-bit registers: addr & count in one!
|
||||
__IO uint32_t USB_ADDR_COUNT_TX;
|
||||
__IO uint32_t USB_ADDR_COUNT_RX;
|
||||
#define ACCESSZ (1)
|
||||
#else
|
||||
#error "Define USB1_16 or USB2_16"
|
||||
#error "Define USB1_16 (16 bits over 32bit register), USB2_16 (16 bits over 16 bit register) or USB32 (32 bist over 32 bit register)"
|
||||
#endif
|
||||
} USB_EPDATA_TypeDef;
|
||||
|
||||
@@ -303,9 +323,17 @@ typedef struct {
|
||||
|
||||
// endpoints state
|
||||
typedef struct{
|
||||
#ifdef USB32
|
||||
uint32_t *tx_buf; // transmission buffer address
|
||||
#else
|
||||
uint16_t *tx_buf; // transmission buffer address
|
||||
#endif
|
||||
uint16_t txbufsz; // transmission buffer size
|
||||
#ifdef USB32
|
||||
uint32_t *rx_buf; // reception buffer address
|
||||
#else
|
||||
uint8_t *rx_buf; // reception buffer address
|
||||
#endif
|
||||
void (*func)(); // endpoint action function
|
||||
unsigned rx_cnt : 10; // received data counter
|
||||
} ep_t;
|
||||
@@ -317,6 +345,7 @@ int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*f
|
||||
void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size);
|
||||
void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size);
|
||||
int EP_Read(uint8_t number, uint8_t *buf);
|
||||
void EP_reset(uint8_t epno);
|
||||
|
||||
// could be [re]defined in usb_dev.c
|
||||
extern void usb_class_request(config_pack_t *packet, uint8_t *data, uint16_t datalen);
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#define BUILD_NUMBER "144"
|
||||
#define BUILD_DATE "2026-02-13"
|
||||
#define BUILD_NUMBER "147"
|
||||
#define BUILD_DATE "2026-03-23"
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "hardware.h"
|
||||
#include "proto.h"
|
||||
#include "spi.h"
|
||||
#include "strfunc.h"
|
||||
#include "usb_dev.h"
|
||||
|
||||
#define CU(a) ((const uint8_t*)a)
|
||||
@@ -171,6 +172,7 @@ void canon_init(){
|
||||
*/
|
||||
void canon_proc(){
|
||||
static uint32_t Tconn = 0;
|
||||
if(state == LENS_DISABLED) return;
|
||||
if(state == LENS_DISCONNECTED){
|
||||
if(!LENSCONNECTED()){
|
||||
Tconn = 0;
|
||||
@@ -315,7 +317,7 @@ void canon_proc(){
|
||||
/*
|
||||
case CANON_GETFOCM: // don't work @EF200
|
||||
uval = (rbuf[1] << 24) | (rbuf[2] << 16) | (rbuf[3] << 8) | rbuf[4];
|
||||
USB_send("Fval="); USB_send(u2str(uval)); USB_send("\n");
|
||||
USB_sendstr("Fval="); USB_sendstr(u2str(uval)); USB_sendstr("\n");
|
||||
break;
|
||||
*/
|
||||
}
|
||||
@@ -393,7 +395,7 @@ int canon_asku16(uint8_t cmd){
|
||||
int canon_getinfo(){
|
||||
if(!ready || !canon_read(CANON_GETINFO, 6)) return 1;
|
||||
USB_sendstr("Info="); for(int i = 1; i < 7; ++i){
|
||||
USB_sendstr(u2hexstr(buf[i])); USB_sendstr(" ");
|
||||
USB_sendstr(uhex2str(buf[i])); USB_sendstr(" ");
|
||||
}
|
||||
//canon_poll();
|
||||
USB_sendstr("\n");
|
||||
@@ -403,3 +405,25 @@ int canon_getinfo(){
|
||||
uint16_t canon_getstate(){
|
||||
return state | (inistate << 8);
|
||||
}
|
||||
|
||||
void canon_disable(){
|
||||
if(state == LENS_DISABLED) return;
|
||||
state = LENS_DISABLED;
|
||||
LENS_OFF();
|
||||
ready = 0;
|
||||
}
|
||||
|
||||
void canon_enable(){
|
||||
if(state != LENS_DISABLED) return;
|
||||
if(OVERCURRENT()){
|
||||
state = LENS_OVERCURRENT;
|
||||
return;
|
||||
}
|
||||
if(!LENSCONNECTED()){
|
||||
state = LENS_DISCONNECTED;
|
||||
return;
|
||||
}
|
||||
LENS_ON();
|
||||
state = LENS_SLEEPING;
|
||||
ready = 1;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stm32f1.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// all data sent in big-endian format
|
||||
|
||||
@@ -92,6 +92,7 @@ typedef enum{
|
||||
LENS_INITIALIZED, // initializing process
|
||||
LENS_READY, // ready to operate
|
||||
LENS_ERR, // some error occured - reconnect after REINIT_PAUSE
|
||||
LENS_DISABLED, // powered off by command
|
||||
LENS_S_AMOUNT
|
||||
} lens_state;
|
||||
|
||||
@@ -109,6 +110,8 @@ typedef enum{
|
||||
|
||||
void canon_init();
|
||||
void canon_proc();
|
||||
void canon_disable();
|
||||
void canon_enable();
|
||||
int canon_diaphragm(char command);
|
||||
int canon_focus(int16_t val);
|
||||
int canon_sendcmd(uint8_t cmd);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 15.0.1, 2025-03-11T16:16:01. -->
|
||||
<!-- Written by QtCreator 18.0.0, 2026-04-14T09:27:26. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
@@ -13,8 +13,8 @@
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="bool" key="EditorConfiguration.AutoDetect">true</value>
|
||||
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
|
||||
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||
<value type="QString" key="language">Cpp</value>
|
||||
@@ -86,12 +86,14 @@
|
||||
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
||||
</valuemap>
|
||||
<value type="int" key="RcSync">0</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="QString" key="DeviceType">Desktop</value>
|
||||
<value type="bool" key="HasPerBcDcs">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{91347f2c-5221-46a7-80b1-0a054ca02f79}</value>
|
||||
@@ -109,8 +111,8 @@
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
@@ -122,8 +124,8 @@
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
@@ -133,13 +135,46 @@
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">По умолчанию</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
@@ -161,6 +196,7 @@
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
</valuemap>
|
||||
@@ -171,10 +207,6 @@
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="qlonglong">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>Version</variable>
|
||||
<value type="int">22</value>
|
||||
|
||||
@@ -13,6 +13,8 @@ ringbuffer.c
|
||||
ringbuffer.h
|
||||
spi.c
|
||||
spi.h
|
||||
strfunc.c
|
||||
strfunc.h
|
||||
usb.c
|
||||
usb.h
|
||||
usb_descr.c
|
||||
|
||||
Binary file not shown.
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "flash.h"
|
||||
#include "proto.h"
|
||||
#include "strfunc.h"
|
||||
#include "usb_dev.h" // printout
|
||||
#include <string.h> // memcpy
|
||||
|
||||
@@ -145,7 +146,7 @@ static int write2flash(const void *start, const void *wrdata, uint32_t stor_size
|
||||
}
|
||||
#ifdef EBUG
|
||||
USB_sendstr(u2str(stor_size)); USB_sendstr("bytes stored @0x");
|
||||
USB_sendstr(u2hexstr((uint32_t)(address + i))); USB_sendstr("\n");
|
||||
USB_sendstr(uhex2str((uint32_t)(address + i))); USB_sendstr("\n");
|
||||
#endif
|
||||
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR;
|
||||
}
|
||||
|
||||
@@ -48,14 +48,13 @@ int main(void){
|
||||
|
||||
uint32_t SPIctr = Tms;
|
||||
while(1){
|
||||
// TODO: add CAN bus parsing
|
||||
IWDG->KR = IWDG_REFRESH;
|
||||
int l = USB_receivestr(inbuff, 255);
|
||||
if(l > 0){
|
||||
parse_cmd(inbuff); // call it even for NULL (if `flood` is running)
|
||||
if(Tms - SPIctr > CANONPROC_INTERVAL){ // not more than once per 10ms
|
||||
SPIctr = Tms;
|
||||
canon_proc();
|
||||
}
|
||||
if(Tms - SPIctr > CANONPROC_INTERVAL){ // not more than once per 10ms
|
||||
SPIctr = Tms;
|
||||
canon_proc();
|
||||
}
|
||||
int l = USB_receivestr(inbuff, 255);
|
||||
if(l > 0) parse_cmd(inbuff); // call it even for NULL (if `flood` is running)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,143 +23,16 @@
|
||||
#include "hardware.h"
|
||||
#include "proto.h"
|
||||
#include "spi.h"
|
||||
#include "strfunc.h"
|
||||
#include "usb_dev.h"
|
||||
#include "version.inc"
|
||||
|
||||
static const char *OK = "OK", *FAIL = "FAIL";
|
||||
|
||||
char *omit_spaces(const char *buf){
|
||||
while(*buf){
|
||||
if(*buf > ' ') break;
|
||||
++buf;
|
||||
}
|
||||
return (char*)buf;
|
||||
}
|
||||
|
||||
// In case of overflow return `buf` and N==0xffffffff
|
||||
// read decimal number & return pointer to next non-number symbol
|
||||
static char *getdec(const char *buf, uint32_t *N){
|
||||
char *start = (char*)buf;
|
||||
uint32_t num = 0;
|
||||
while(*buf){
|
||||
char c = *buf;
|
||||
if(c < '0' || c > '9'){
|
||||
break;
|
||||
}
|
||||
if(num > 429496729 || (num == 429496729 && c > '5')){ // overflow
|
||||
*N = 0xffffff;
|
||||
return start;
|
||||
}
|
||||
num *= 10;
|
||||
num += c - '0';
|
||||
++buf;
|
||||
}
|
||||
*N = num;
|
||||
return (char*)buf;
|
||||
}
|
||||
// read hexadecimal number (without 0x prefix!)
|
||||
static char *gethex(const char *buf, uint32_t *N){
|
||||
char *start = (char*)buf;
|
||||
uint32_t num = 0;
|
||||
while(*buf){
|
||||
char c = *buf;
|
||||
uint8_t M = 0;
|
||||
if(c >= '0' && c <= '9'){
|
||||
M = '0';
|
||||
}else if(c >= 'A' && c <= 'F'){
|
||||
M = 'A' - 10;
|
||||
}else if(c >= 'a' && c <= 'f'){
|
||||
M = 'a' - 10;
|
||||
}
|
||||
if(M){
|
||||
if(num & 0xf0000000){ // overflow
|
||||
*N = 0xffffff;
|
||||
return start;
|
||||
}
|
||||
num <<= 4;
|
||||
num += c - M;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
++buf;
|
||||
}
|
||||
*N = num;
|
||||
return (char*)buf;
|
||||
}
|
||||
// read octal number (without 0 prefix!)
|
||||
static char *getoct(const char *buf, uint32_t *N){
|
||||
char *start = (char*)buf;
|
||||
uint32_t num = 0;
|
||||
while(*buf){
|
||||
char c = *buf;
|
||||
if(c < '0' || c > '7'){
|
||||
break;
|
||||
}
|
||||
if(num & 0xe0000000){ // overflow
|
||||
*N = 0xffffff;
|
||||
return start;
|
||||
}
|
||||
num <<= 3;
|
||||
num += c - '0';
|
||||
++buf;
|
||||
}
|
||||
*N = num;
|
||||
return (char*)buf;
|
||||
}
|
||||
// read binary number (without b prefix!)
|
||||
static char *getbin(const char *buf, uint32_t *N){
|
||||
char *start = (char*)buf;
|
||||
uint32_t num = 0;
|
||||
while(*buf){
|
||||
char c = *buf;
|
||||
if(c < '0' || c > '1'){
|
||||
break;
|
||||
}
|
||||
if(num & 0x80000000){ // overflow
|
||||
*N = 0xffffff;
|
||||
return start;
|
||||
}
|
||||
num <<= 1;
|
||||
if(c == '1') num |= 1;
|
||||
++buf;
|
||||
}
|
||||
*N = num;
|
||||
return (char*)buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief getnum - read uint32_t from string (dec, hex or bin: 127, 0x7f, 0b1111111)
|
||||
* @param buf - buffer with number and so on
|
||||
* @param N - the number read
|
||||
* @return pointer to first non-number symbol in buf
|
||||
* (if it is == buf, there's no number or if *N==0xffffffff there was overflow)
|
||||
*/
|
||||
char *getnum(const char *txt, uint32_t *N){
|
||||
char *nxt = NULL;
|
||||
char *s = omit_spaces(txt);
|
||||
if(*s == '0'){ // hex, oct or 0
|
||||
if(s[1] == 'x' || s[1] == 'X'){ // hex
|
||||
nxt = gethex(s+2, N);
|
||||
if(nxt == s+2) nxt = (char*)txt;
|
||||
}else if(s[1] > '0'-1 && s[1] < '8'){ // oct
|
||||
nxt = getoct(s+1, N);
|
||||
if(nxt == s+1) nxt = (char*)txt;
|
||||
}else{ // 0
|
||||
nxt = s+1;
|
||||
*N = 0;
|
||||
}
|
||||
}else if(*s == 'b' || *s == 'B'){
|
||||
nxt = getbin(s+1, N);
|
||||
if(nxt == s+1) nxt = (char*)txt;
|
||||
}else{
|
||||
nxt = getdec(s, N);
|
||||
if(nxt == s) nxt = (char*)txt;
|
||||
}
|
||||
return nxt;
|
||||
}
|
||||
|
||||
const char* helpmsg =
|
||||
"https://github.com/eddyem/stm32samples/tree/master/F1-nolib/Canon_managing_device build#" BUILD_NUMBER " @ " BUILD_DATE "\n"
|
||||
"# - turn off lens power\n"
|
||||
"* - turn on lens power\n"
|
||||
"0 - move to smallest foc value (e.g. 2.5m)\n"
|
||||
"1 - move to largest foc value (e.g. infinity)\n"
|
||||
"a - move focus to given ABSOLUTE position or get current value (without number)\n"
|
||||
@@ -213,7 +86,7 @@ static void sendspibuf(){
|
||||
USB_sendstr("Got SPI answer: ");
|
||||
for(int i = 0; i < spibuflen; ++i){
|
||||
if(i) USB_sendstr(", ");
|
||||
USB_sendstr(u2hexstr(buf[i]));
|
||||
USB_sendstr(uhex2str(buf[i]));
|
||||
}
|
||||
USB_sendstr("\n");
|
||||
}else USB_sendstr("Failed to send SPI buffer\n");
|
||||
@@ -234,6 +107,7 @@ const char *connmsgs[LENS_S_AMOUNT+1] = {
|
||||
[LENS_INITIALIZED] = "initialized",
|
||||
[LENS_READY] = "ready",
|
||||
[LENS_ERR] = "error",
|
||||
[LENS_DISABLED] = "turned off",
|
||||
[LENS_S_AMOUNT] = "wrong state"
|
||||
};
|
||||
const char *inimsgs[INI_S_AMOUNT+1] = {
|
||||
@@ -257,6 +131,14 @@ void parse_cmd(const char *buf){
|
||||
lastFloodTime= FALSE;
|
||||
if(buf[1] == '\n' || !buf[1]){ // one symbol commands
|
||||
switch(*buf){
|
||||
case '*':
|
||||
canon_enable();
|
||||
USB_sendstr(OK);
|
||||
break;
|
||||
case '#':
|
||||
canon_disable();
|
||||
USB_sendstr(OK);
|
||||
break;
|
||||
case 'a':
|
||||
case 'f':
|
||||
errw(canon_focus(-1));
|
||||
@@ -284,7 +166,7 @@ void parse_cmd(const char *buf){
|
||||
USB_sendstr(OK);
|
||||
break;
|
||||
case 'F': // just watch SPI->CR1 value
|
||||
USB_sendstr("SPI1->CR1="); USB_sendstr(u2hexstr(SPI_CR1));
|
||||
USB_sendstr("SPI1->CR1="); USB_sendstr(uhex2str(SPI_CR1));
|
||||
break;
|
||||
case 'G':
|
||||
USB_sendstr("SPI ");
|
||||
@@ -344,7 +226,7 @@ void parse_cmd(const char *buf){
|
||||
}
|
||||
uint32_t D = 0;
|
||||
int16_t neg;
|
||||
char *nxt;
|
||||
const char *nxt;
|
||||
switch(*buf++){ // long messages
|
||||
case 'a': // move focus to absolute position
|
||||
buf = omit_spaces(buf);
|
||||
@@ -425,7 +307,7 @@ void parse_cmd(const char *buf){
|
||||
USB_sendstr(helpmsg);
|
||||
return;
|
||||
}
|
||||
USB_sendstr("SPI_CR1="); USB_sendstr(u2hexstr(SPI_CR1));
|
||||
USB_sendstr("SPI_CR1="); USB_sendstr(uhex2str(SPI_CR1));
|
||||
break;
|
||||
case 'L':
|
||||
if(0 == initspibuf(buf)){
|
||||
@@ -445,7 +327,7 @@ void parse_cmd(const char *buf){
|
||||
USB_sendstr("Send: ");
|
||||
for(int i = 0; i < spibuflen; ++i){
|
||||
if(i) USB_sendstr(", ");
|
||||
USB_sendstr(u2hexstr(spibuf[i]));
|
||||
USB_sendstr(uhex2str(spibuf[i]));
|
||||
}
|
||||
USB_sendstr("\n");
|
||||
sendspibuf();
|
||||
@@ -456,41 +338,3 @@ void parse_cmd(const char *buf){
|
||||
}
|
||||
newline();
|
||||
}
|
||||
|
||||
|
||||
// return string with number `val`
|
||||
char *u2str(uint32_t val){
|
||||
static char strbuf[11];
|
||||
char *bufptr = &strbuf[10];
|
||||
*bufptr = 0;
|
||||
if(!val){
|
||||
*(--bufptr) = '0';
|
||||
}else{
|
||||
while(val){
|
||||
*(--bufptr) = val % 10 + '0';
|
||||
val /= 10;
|
||||
}
|
||||
}
|
||||
return bufptr;
|
||||
}
|
||||
|
||||
char *u2hexstr(uint32_t val){
|
||||
static char strbuf[11] = "0x";
|
||||
char *sptr = strbuf + 2;
|
||||
uint8_t *ptr = (uint8_t*)&val + 3;
|
||||
int8_t i, j, z=1;
|
||||
for(i = 0; i < 4; ++i, --ptr){
|
||||
if(*ptr == 0){ // omit leading zeros
|
||||
if(i == 3) z = 0;
|
||||
if(z) continue;
|
||||
}
|
||||
else z = 0;
|
||||
for(j = 1; j > -1; --j){
|
||||
uint8_t half = (*ptr >> (4*j)) & 0x0f;
|
||||
if(half < 10) *sptr++ = half + '0';
|
||||
else *sptr++ = half - 10 + 'a';
|
||||
}
|
||||
}
|
||||
*sptr = 0;
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
@@ -17,24 +17,15 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef PROTO_H__
|
||||
#define PROTO_H__
|
||||
|
||||
#include <stm32f1.h>
|
||||
|
||||
#define printu(x) do{USB_sendstr(u2str(x));}while(0)
|
||||
#define printuhex(x) do{USB_sendstr(uhex2str(x));}while(0)
|
||||
|
||||
#ifdef EBUG
|
||||
#define DBG(x) do{USB_send(x); USB_send("\n");}while(0)
|
||||
#define DBG(x) do{USB_sendstr(x); newline();}while(0)
|
||||
#else
|
||||
#define DBG(x)
|
||||
#endif
|
||||
|
||||
void parse_cmd(const char *buf);
|
||||
char *omit_spaces(const char *buf);
|
||||
char *getnum(const char *buf, uint32_t *N);
|
||||
char *u2str(uint32_t val);
|
||||
char *u2hexstr(uint32_t val);
|
||||
|
||||
#endif // PROTO_H__
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stm32f1.h>
|
||||
#include "ringbuffer.h"
|
||||
|
||||
static int datalen(ringbuffer *b){
|
||||
@@ -24,7 +26,7 @@ static int datalen(ringbuffer *b){
|
||||
|
||||
// stored data length
|
||||
int RB_datalen(ringbuffer *b){
|
||||
if(b->busy) return -1;
|
||||
if(!b || b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int l = datalen(b);
|
||||
b->busy = 0;
|
||||
@@ -32,7 +34,7 @@ int RB_datalen(ringbuffer *b){
|
||||
}
|
||||
|
||||
static int hasbyte(ringbuffer *b, uint8_t byte){
|
||||
if(b->head == b->tail) return -1; // no data in buffer
|
||||
if(!b || b->head == b->tail) return -1; // no data in buffer
|
||||
int startidx = b->head;
|
||||
if(b->head > b->tail){ //
|
||||
for(int found = b->head; found < b->length; ++found)
|
||||
@@ -51,18 +53,13 @@ static int hasbyte(ringbuffer *b, uint8_t byte){
|
||||
* @return index if found, -1 if none or busy
|
||||
*/
|
||||
int RB_hasbyte(ringbuffer *b, uint8_t byte){
|
||||
if(b->busy) return -1;
|
||||
if(!b || b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int ret = hasbyte(b, byte);
|
||||
b->busy = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// poor memcpy
|
||||
static void mcpy(uint8_t *targ, const uint8_t *src, int l){
|
||||
while(l--) *targ++ = *src++;
|
||||
}
|
||||
|
||||
// increment head or tail
|
||||
TRUE_INLINE void incr(ringbuffer *b, volatile int *what, int n){
|
||||
*what += n;
|
||||
@@ -76,9 +73,9 @@ static int read(ringbuffer *b, uint8_t *s, int len){
|
||||
int _1st = b->length - b->head;
|
||||
if(_1st > l) _1st = l;
|
||||
if(_1st > len) _1st = len;
|
||||
mcpy(s, b->data + b->head, _1st);
|
||||
memcpy(s, b->data + b->head, _1st);
|
||||
if(_1st < len && l > _1st){
|
||||
mcpy(s+_1st, b->data, l - _1st);
|
||||
memcpy(s+_1st, b->data, l - _1st);
|
||||
incr(b, &b->head, l);
|
||||
return l;
|
||||
}
|
||||
@@ -94,20 +91,27 @@ static int read(ringbuffer *b, uint8_t *s, int len){
|
||||
* @return bytes read or -1 if busy
|
||||
*/
|
||||
int RB_read(ringbuffer *b, uint8_t *s, int len){
|
||||
if(b->busy) return -1;
|
||||
if(!b || b->busy || !s || len < 1) return -1;
|
||||
b->busy = 1;
|
||||
int r = read(b, s, len);
|
||||
b->busy = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
|
||||
// length of data from current position to `byte` (including byte)
|
||||
static int lento(ringbuffer *b, uint8_t byte){
|
||||
int idx = hasbyte(b, byte);
|
||||
if(idx < 0) return 0;
|
||||
int partlen = idx + 1 - b->head;
|
||||
// now calculate length of new data portion
|
||||
if(idx < b->head) partlen += b->length;
|
||||
if(partlen > len) return -read(b, s, len);
|
||||
return partlen;
|
||||
}
|
||||
|
||||
static int readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
|
||||
int partlen = lento(b, byte);
|
||||
if(!partlen) return 0;
|
||||
if(partlen > len) return -1;
|
||||
return read(b, s, partlen);
|
||||
}
|
||||
|
||||
@@ -115,26 +119,41 @@ static int readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
|
||||
* @brief RB_readto fill array `s` with data until byte `byte` (with it)
|
||||
* @param b - ringbuffer
|
||||
* @param byte - check byte
|
||||
* @param s - buffer to write data
|
||||
* @param len - length of `s`
|
||||
* @param s - buffer to write data or NULL to clear data
|
||||
* @param len - length of `s` or 0 to clear data
|
||||
* @return amount of bytes written (negative, if len<data in buffer or buffer is busy)
|
||||
*/
|
||||
int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
|
||||
if(b->busy) return -1;
|
||||
if(!b || b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int n = readto(b, byte, s, len);
|
||||
int n = 0;
|
||||
if(s && len > 0){
|
||||
n = readto(b, byte, s, len);
|
||||
}else{
|
||||
incr(b, &b->head, lento(b, byte)); // just throw data out
|
||||
}
|
||||
b->busy = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
int RB_datalento(ringbuffer *b, uint8_t byte){
|
||||
if(!b || b->busy) return -1;
|
||||
b->busy = 1;
|
||||
int n = lento(b, byte);
|
||||
b->busy = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
// if l < rest of buffer, truncate and return actually written bytes
|
||||
static int write(ringbuffer *b, const uint8_t *str, int l){
|
||||
int r = b->length - 1 - datalen(b); // rest length
|
||||
if(l > r || !l) return 0;
|
||||
if(r < 1) return 0;
|
||||
if(l > r) l = r;
|
||||
int _1st = b->length - b->tail;
|
||||
if(_1st > l) _1st = l;
|
||||
mcpy(b->data + b->tail, str, _1st);
|
||||
memcpy(b->data + b->tail, str, _1st);
|
||||
if(_1st < l){ // add another piece from start
|
||||
mcpy(b->data, str+_1st, l-_1st);
|
||||
memcpy(b->data, str+_1st, l-_1st);
|
||||
}
|
||||
incr(b, &b->tail, l);
|
||||
return l;
|
||||
@@ -148,7 +167,7 @@ static int write(ringbuffer *b, const uint8_t *str, int l){
|
||||
* @return amount of bytes written or -1 if busy
|
||||
*/
|
||||
int RB_write(ringbuffer *b, const uint8_t *str, int l){
|
||||
if(b->busy) return -1;
|
||||
if(!b || b->busy || !str || l < 1) return -1;
|
||||
b->busy = 1;
|
||||
int w = write(b, str, l);
|
||||
b->busy = 0;
|
||||
@@ -157,7 +176,7 @@ int RB_write(ringbuffer *b, const uint8_t *str, int l){
|
||||
|
||||
// just delete all information in buffer `b`
|
||||
int RB_clearbuf(ringbuffer *b){
|
||||
if(b->busy) return -1;
|
||||
if(!b || b->busy) return -1;
|
||||
b->busy = 1;
|
||||
b->head = 0;
|
||||
b->tail = 0;
|
||||
|
||||
@@ -17,13 +17,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined STM32F0
|
||||
#include <stm32f0.h>
|
||||
#elif defined STM32F1
|
||||
#include <stm32f1.h>
|
||||
#elif defined STM32F3
|
||||
#include <stm32f3.h>
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct{
|
||||
uint8_t *data; // data buffer
|
||||
@@ -38,4 +32,5 @@ int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len);
|
||||
int RB_hasbyte(ringbuffer *b, uint8_t byte);
|
||||
int RB_write(ringbuffer *b, const uint8_t *str, int l);
|
||||
int RB_datalen(ringbuffer *b);
|
||||
int RB_datalento(ringbuffer *b, uint8_t byte);
|
||||
int RB_clearbuf(ringbuffer *b);
|
||||
|
||||
@@ -21,9 +21,10 @@
|
||||
#include "spi.h"
|
||||
#include "hardware.h"
|
||||
#ifdef EBUG
|
||||
#include "usb.h"
|
||||
#include "usb_dev.h"
|
||||
#endif
|
||||
#include "proto.h"
|
||||
#include "strfunc.h"
|
||||
|
||||
/*
|
||||
static void mymemcpy(uint8_t *dest, uint8_t *src, int len){
|
||||
@@ -56,26 +57,26 @@ uint8_t SPI_transmit(uint8_t *buf, uint8_t len){
|
||||
if(!buf || !len) return 0; // bad data format
|
||||
if(SPI_status != SPI_READY) return 0; // spi not ready to transmit data
|
||||
#ifdef EBUG
|
||||
USB_send("SPI send "); USB_send(u2str(len));
|
||||
USB_send("bytes, data: ");
|
||||
USB_sendstr("SPI send "); USB_sendstr(u2str(len));
|
||||
USB_sendstr("bytes, data: ");
|
||||
#endif
|
||||
for(uint8_t x = 0; x < len; ++x){
|
||||
WAITX(!(SPI1->SR & SPI_SR_TXE));
|
||||
SPI1->DR = buf[x];
|
||||
WAITX(!(SPI1->SR & SPI_SR_BSY));
|
||||
#ifdef EBUG
|
||||
USB_send(u2hexstr(buf[x])); USB_send(", ");
|
||||
USB_sendstr(uhex2str(buf[x])); USB_sendstr(", ");
|
||||
#endif
|
||||
WAITX(!(SPI1->SR & SPI_SR_RXNE));
|
||||
buf[x] = SPI1->DR;
|
||||
for(int ctr = 0; ctr < 3600; ++ctr) IWDG->KR = IWDG_REFRESH; // ~100mks delay
|
||||
}
|
||||
#ifdef EBUG
|
||||
USB_send("\nReceive: ");
|
||||
USB_sendstr("\nReceive: ");
|
||||
for(int i = 0; i < len; ++i){
|
||||
USB_send(u2hexstr(buf[i])); USB_send(", ");
|
||||
USB_sendstr(uhex2str(buf[i])); USB_sendstr(", ");
|
||||
}
|
||||
USB_send("\n");
|
||||
USB_sendstr("\n");
|
||||
#endif
|
||||
return len;
|
||||
}
|
||||
|
||||
265
F1:F103/Canon_managing_device/strfunc.c
Normal file
265
F1:F103/Canon_managing_device/strfunc.c
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* This file is part of the test project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "strfunc.h"
|
||||
|
||||
/**
|
||||
* @brief hexdump - dump hex array by 16 bytes in string
|
||||
* @param sendfun - function to send data
|
||||
* @param arr - array to dump
|
||||
* @param len - length of `arr`
|
||||
*/
|
||||
void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len){
|
||||
char buf[52], *bptr = buf;
|
||||
for(uint16_t l = 0; l < len; ++l, ++arr){
|
||||
for(int16_t j = 1; j > -1; --j){
|
||||
register uint8_t half = (*arr >> (4*j)) & 0x0f;
|
||||
if(half < 10) *bptr++ = half + '0';
|
||||
else *bptr++ = half - 10 + 'a';
|
||||
}
|
||||
if(l % 16 == 15){
|
||||
*bptr++ = '\n';
|
||||
*bptr = 0;
|
||||
sendfun(buf);
|
||||
bptr = buf;
|
||||
}else *bptr++ = ' ';
|
||||
}
|
||||
if(bptr != buf){
|
||||
*bptr++ = '\n';
|
||||
*bptr = 0;
|
||||
sendfun(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief _2str - convert value into string buffer
|
||||
* @param val - |value|
|
||||
* @param minus - ==0 if value > 0
|
||||
* @return buffer with number
|
||||
*/
|
||||
static const char *_2str(uint32_t val, uint8_t minus){
|
||||
static char strbuf[12];
|
||||
char *bufptr = &strbuf[11];
|
||||
*bufptr = 0;
|
||||
if(!val){
|
||||
*(--bufptr) = '0';
|
||||
}else{
|
||||
while(val){
|
||||
uint32_t x = val / 10;
|
||||
*(--bufptr) = (val - 10*x) + '0';
|
||||
val = x;
|
||||
}
|
||||
}
|
||||
if(minus) *(--bufptr) = '-';
|
||||
return bufptr;
|
||||
}
|
||||
|
||||
// return string with number `val`
|
||||
const char *u2str(uint32_t val){
|
||||
return _2str(val, 0);
|
||||
}
|
||||
const char *i2str(int32_t i){
|
||||
uint8_t minus = 0;
|
||||
uint32_t val;
|
||||
if(i < 0){
|
||||
minus = 1;
|
||||
val = -i;
|
||||
}else val = i;
|
||||
return _2str(val, minus);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief uhex2str - print 32bit unsigned int as hex
|
||||
* @param val - value
|
||||
* @return string with number
|
||||
*/
|
||||
const char *uhex2str(uint32_t val){
|
||||
static char buf[12] = "0x";
|
||||
int npos = 2;
|
||||
uint8_t *ptr = (uint8_t*)&val + 3;
|
||||
int8_t i, j, z=1;
|
||||
for(i = 0; i < 4; ++i, --ptr){
|
||||
if(*ptr == 0){ // omit leading zeros
|
||||
if(i == 3) z = 0;
|
||||
if(z) continue;
|
||||
}
|
||||
else z = 0;
|
||||
for(j = 1; j > -1; --j){
|
||||
uint8_t half = (*ptr >> (4*j)) & 0x0f;
|
||||
if(half < 10) buf[npos++] = half + '0';
|
||||
else buf[npos++] = half - 10 + 'a';
|
||||
}
|
||||
}
|
||||
buf[npos] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief omit_spaces - eliminate leading spaces and other trash in string
|
||||
* @param buf - string
|
||||
* @return - pointer to first character in `buf` > ' '
|
||||
*/
|
||||
const char *omit_spaces(const char *buf){
|
||||
while(*buf){
|
||||
if(*buf > ' ') break;
|
||||
++buf;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief getdec - read decimal number & return pointer to next non-number symbol
|
||||
* @param buf - string
|
||||
* @param N - number read
|
||||
* @return Next non-number symbol. In case of overflow return `buf` and N==0xffffffff
|
||||
*/
|
||||
static const char *getdec(const char *buf, uint32_t *N){
|
||||
char *start = (char*)buf;
|
||||
uint32_t num = 0;
|
||||
while(*buf){
|
||||
char c = *buf;
|
||||
if(c < '0' || c > '9'){
|
||||
break;
|
||||
}
|
||||
if(num > 429496729 || (num == 429496729 && c > '5')){ // overflow
|
||||
*N = 0xffffff;
|
||||
return start;
|
||||
}
|
||||
num *= 10;
|
||||
num += c - '0';
|
||||
++buf;
|
||||
}
|
||||
*N = num;
|
||||
return buf;
|
||||
}
|
||||
// read hexadecimal number (without 0x prefix!)
|
||||
static const char *gethex(const char *buf, uint32_t *N){
|
||||
const char *start = buf;
|
||||
uint32_t num = 0;
|
||||
while(*buf){
|
||||
char c = *buf;
|
||||
uint8_t M = 0;
|
||||
if(c >= '0' && c <= '9'){
|
||||
M = '0';
|
||||
}else if(c >= 'A' && c <= 'F'){
|
||||
M = 'A' - 10;
|
||||
}else if(c >= 'a' && c <= 'f'){
|
||||
M = 'a' - 10;
|
||||
}
|
||||
if(M){
|
||||
if(num & 0xf0000000){ // overflow
|
||||
*N = 0xffffff;
|
||||
return start;
|
||||
}
|
||||
num <<= 4;
|
||||
num += c - M;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
++buf;
|
||||
}
|
||||
*N = num;
|
||||
return buf;
|
||||
}
|
||||
// read octal number (without 0 prefix!)
|
||||
static const char *getoct(const char *buf, uint32_t *N){
|
||||
const char *start = (char*)buf;
|
||||
uint32_t num = 0;
|
||||
while(*buf){
|
||||
char c = *buf;
|
||||
if(c < '0' || c > '7'){
|
||||
break;
|
||||
}
|
||||
if(num & 0xe0000000){ // overflow
|
||||
*N = 0xffffff;
|
||||
return start;
|
||||
}
|
||||
num <<= 3;
|
||||
num += c - '0';
|
||||
++buf;
|
||||
}
|
||||
*N = num;
|
||||
return buf;
|
||||
}
|
||||
// read binary number (without b prefix!)
|
||||
static const char *getbin(const char *buf, uint32_t *N){
|
||||
const char *start = (char*)buf;
|
||||
uint32_t num = 0;
|
||||
while(*buf){
|
||||
char c = *buf;
|
||||
if(c < '0' || c > '1'){
|
||||
break;
|
||||
}
|
||||
if(num & 0x80000000){ // overflow
|
||||
*N = 0xffffff;
|
||||
return start;
|
||||
}
|
||||
num <<= 1;
|
||||
if(c == '1') num |= 1;
|
||||
++buf;
|
||||
}
|
||||
*N = num;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief getnum - read uint32_t from string (dec, hex or bin: 127, 0x7f, 0b1111111)
|
||||
* @param buf - buffer with number and so on
|
||||
* @param N - the number read
|
||||
* @return pointer to first non-number symbol in buf
|
||||
* (if it is == buf, there's no number or if *N==0xffffffff there was overflow)
|
||||
*/
|
||||
const char *getnum(const char *txt, uint32_t *N){
|
||||
const char *nxt = NULL;
|
||||
const char *s = omit_spaces(txt);
|
||||
if(*s == '0'){ // hex, oct or 0
|
||||
if(s[1] == 'x' || s[1] == 'X'){ // hex
|
||||
nxt = gethex(s+2, N);
|
||||
if(nxt == s+2) nxt = (char*)txt;
|
||||
}else if(s[1] > '0'-1 && s[1] < '8'){ // oct
|
||||
nxt = getoct(s+1, N);
|
||||
if(nxt == s+1) nxt = (char*)txt;
|
||||
}else{ // 0
|
||||
nxt = s+1;
|
||||
*N = 0;
|
||||
}
|
||||
}else if(*s == 'b' || *s == 'B'){
|
||||
nxt = getbin(s+1, N);
|
||||
if(nxt == s+1) nxt = (char*)txt;
|
||||
}else{
|
||||
nxt = getdec(s, N);
|
||||
if(nxt == s) nxt = (char*)txt;
|
||||
}
|
||||
return nxt;
|
||||
}
|
||||
|
||||
// get signed integer
|
||||
const char *getint(const char *txt, int32_t *I){
|
||||
const char *s = omit_spaces(txt);
|
||||
int32_t sign = 1;
|
||||
uint32_t U;
|
||||
if(*s == '-'){
|
||||
sign = -1;
|
||||
++s;
|
||||
}
|
||||
const char *nxt = getnum(s, &U);
|
||||
if(nxt == s) return txt;
|
||||
if(U & 0x80000000) return txt; // overfull
|
||||
*I = sign * (int32_t)U;
|
||||
return nxt;
|
||||
}
|
||||
30
F1:F103/Canon_managing_device/strfunc.h
Normal file
30
F1:F103/Canon_managing_device/strfunc.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* This file is part of the test project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len);
|
||||
const char *u2str(uint32_t val);
|
||||
const char *i2str(int32_t i);
|
||||
const char *uhex2str(uint32_t val);
|
||||
const char *getnum(const char *txt, uint32_t *N);
|
||||
const char *omit_spaces(const char *buf);
|
||||
const char *getint(const char *txt, int32_t *I);
|
||||
@@ -26,7 +26,7 @@ static const uint8_t USB_DeviceDescriptor[] = {
|
||||
USB_DT_DEVICE, // bDescriptorType
|
||||
L16(bcdUSB), // bcdUSB_L
|
||||
H16(bcdUSB), // bcdUSB_H
|
||||
USB_CLASS_COMM, // bDeviceClass
|
||||
USB_CLASS_MISC, // bDeviceClass
|
||||
bDeviceSubClass, // bDeviceSubClass
|
||||
bDeviceProtocol, // bDeviceProtocol
|
||||
USB_EP0BUFSZ, // bMaxPacketSize
|
||||
@@ -140,7 +140,7 @@ static const uint8_t USB_ConfigDescriptor[] = {
|
||||
//const uint8_t HID_ReportDescriptor[];
|
||||
|
||||
_USB_LANG_ID_(LD, LANG_US);
|
||||
_USB_STRING_(SD, u"0.1.0");
|
||||
_USB_STRING_(SD, u"0.1.1");
|
||||
_USB_STRING_(MD, u"eddy@sao.ru");
|
||||
_USB_STRING_(PD, u"USB/CAN Canon lens controller");
|
||||
_USB_STRING_(ID, u"canonlens");
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stm32f1.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ringbuffer.h"
|
||||
@@ -41,10 +40,10 @@
|
||||
static volatile uint8_t bufovrfl = 0;
|
||||
|
||||
// receive buffer: hold data until chkin() call
|
||||
static uint8_t volatile rcvbuf[USB_RXBUFSZ];
|
||||
static uint8_t volatile rcvbuf[USB_RXBUFSZ] __attribute__((aligned(4)));
|
||||
static uint8_t volatile rcvbuflen = 0;
|
||||
// line coding
|
||||
usb_LineCoding WEAK lineCoding = {115200, 0, 0, 8};
|
||||
usb_LineCoding lineCoding = {115200, 0, 0, 8};
|
||||
// CDC configured and ready to use
|
||||
volatile uint8_t CDCready = 0;
|
||||
|
||||
@@ -70,10 +69,10 @@ static void chkin(){
|
||||
|
||||
// called from transmit EP to send next data portion or by user - when new transmission starts
|
||||
static void send_next(){
|
||||
uint8_t usbbuff[USB_TXBUFSZ];
|
||||
uint8_t usbbuff[USB_TXBUFSZ] __attribute__((aligned(4)));
|
||||
int buflen = RB_read((ringbuffer*)&rbout, (uint8_t*)usbbuff, USB_TXBUFSZ);
|
||||
if(buflen == 0){
|
||||
if(lastdsz == 64) EP_Write(1, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
|
||||
if(lastdsz == USB_TXBUFSZ) EP_Write(1, NULL, 0); // send ZLP after USB_TXBUFSZ bits packet when nothing more to send
|
||||
lastdsz = 0;
|
||||
return;
|
||||
}else if(buflen < 0){
|
||||
@@ -103,21 +102,20 @@ static void rxtx_handler(){
|
||||
|
||||
// weak handlers: change them somewhere else if you want to setup USART
|
||||
// SET_LINE_CODING
|
||||
void WEAK linecoding_handler(usb_LineCoding *lc){
|
||||
void linecoding_handler(usb_LineCoding *lc){
|
||||
lineCoding = *lc;
|
||||
}
|
||||
|
||||
// SET_CONTROL_LINE_STATE
|
||||
void WEAK clstate_handler(uint16_t val){
|
||||
void clstate_handler(uint16_t val){
|
||||
CDCready = val; // CONTROL_DTR | CONTROL_RTS -> interface connected; 0 -> disconnected
|
||||
}
|
||||
|
||||
// SEND_BREAK
|
||||
void WEAK break_handler(){
|
||||
void break_handler(){
|
||||
CDCready = 0;
|
||||
}
|
||||
|
||||
|
||||
// USB is configured: setup endpoints
|
||||
void set_configuration(){
|
||||
EP_Init(1, EP_TYPE_BULK, USB_TXBUFSZ, USB_RXBUFSZ, rxtx_handler); // IN1 and OUT1
|
||||
@@ -166,7 +164,16 @@ int USB_sendall(){
|
||||
int USB_send(const uint8_t *buf, int len){
|
||||
if(!buf || !CDCready || !len) return FALSE;
|
||||
while(len){
|
||||
int a = RB_write((ringbuffer*)&rbout, buf, len);
|
||||
IWDG->KR = IWDG_REFRESH;
|
||||
int l = RB_datalen((ringbuffer*)&rbout);
|
||||
if(l < 0) continue;
|
||||
int portion = rbout.length - 1 - l;
|
||||
if(portion < 1){
|
||||
if(lastdsz == 0) send_next();
|
||||
continue;
|
||||
}
|
||||
if(portion > len) portion = len;
|
||||
int a = RB_write((ringbuffer*)&rbout, buf, portion);
|
||||
if(a > 0){
|
||||
len -= a;
|
||||
buf += a;
|
||||
@@ -217,7 +224,7 @@ int USB_receive(uint8_t *buf, int len){
|
||||
* @brief USB_receivestr - get string up to '\n' and replace '\n' with 0
|
||||
* @param buf - receiving buffer
|
||||
* @param len - its length
|
||||
* @return strlen or negative value indicating overflow (if so, string won't be ends with 0 and buffer should be cleared)
|
||||
* @return strlen or negative value indicating overflow
|
||||
*/
|
||||
int USB_receivestr(char *buf, int len){
|
||||
chkin();
|
||||
@@ -226,14 +233,18 @@ int USB_receivestr(char *buf, int len){
|
||||
bufovrfl = 0;
|
||||
return -1;
|
||||
}
|
||||
int l = RB_readto((ringbuffer*)&rbin, '\n', (uint8_t*)buf, len);
|
||||
if(l < 1){
|
||||
int l = RB_datalento((ringbuffer*)&rbin, '\n');
|
||||
if(l > len){ // can't read: line too long -> clear it
|
||||
RB_readto((ringbuffer*)&rbin, '\n', NULL, 0);
|
||||
return -1;
|
||||
}else if(l < 1){ // nothing or no '\n' ?
|
||||
if(rbin.length == RB_datalen((ringbuffer*)&rbin)){ // buffer is full but no '\n' found
|
||||
while(1 != RB_clearbuf((ringbuffer*)&rbin));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
l = RB_readto((ringbuffer*)&rbin, '\n', (uint8_t*)buf, len);
|
||||
if(l == 0) return 0;
|
||||
buf[l-1] = 0; // replace '\n' with strend
|
||||
return l;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stm32f1.h>
|
||||
#include <stdint.h>
|
||||
#include "usb_lib.h"
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
static ep_t endpoints[STM32ENDPOINTS];
|
||||
|
||||
static uint16_t USB_Addr = 0;
|
||||
static uint8_t setupdatabuf[EP0DATABUF_SIZE];
|
||||
static uint8_t setupdatabuf[EP0DATABUF_SIZE] __attribute__((aligned(4)));
|
||||
static config_pack_t *setup_packet = (config_pack_t*) setupdatabuf;
|
||||
volatile uint8_t usbON = 0; // device is configured and active
|
||||
|
||||
@@ -81,8 +81,7 @@ void WEAK usb_standard_request(){
|
||||
break;
|
||||
case REQ_RECIPIENT_ENDPOINT:
|
||||
if(setup_packet->bRequest == CLEAR_FEATURE){
|
||||
}else{
|
||||
}
|
||||
}else{ /* wrong */ }
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -97,7 +96,7 @@ void WEAK usb_class_request(config_pack_t *req, uint8_t _U_ *data, uint16_t _U_
|
||||
case SET_CONFIGURATION: // set featuring by req->wValue
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
break;
|
||||
}
|
||||
if(0 == (setup_packet->bmRequestType & 0x80)) // host2dev
|
||||
EP_WriteIRQ(0, NULL, 0);
|
||||
@@ -119,7 +118,7 @@ bmRequestType: 76543210
|
||||
*/
|
||||
static void EP0_Handler(){
|
||||
uint8_t ep0dbuflen = 0;
|
||||
uint8_t ep0databuf[EP0DATABUF_SIZE];
|
||||
uint8_t ep0databuf[EP0DATABUF_SIZE] __attribute__((aligned(4)));
|
||||
uint16_t epstatus = KEEP_DTOG(USB->EPnR[0]); // EP0R on input -> return this value after modifications
|
||||
int rxflag = RX_FLAG(epstatus);
|
||||
// check direction
|
||||
@@ -128,6 +127,7 @@ static void EP0_Handler(){
|
||||
EP_Read(0, setupdatabuf);
|
||||
// interrupt handler will be called later
|
||||
}else if(epstatus & USB_EPnR_CTR_RX){ // data packet -> push received data to ep0databuf
|
||||
//if(endpoints[0].rx_cnt){ }
|
||||
ep0dbuflen = EP_Read(0, ep0databuf);
|
||||
}
|
||||
}
|
||||
@@ -137,8 +137,7 @@ static void EP0_Handler(){
|
||||
case REQ_TYPE_STANDARD:
|
||||
if(SETUP_FLAG(epstatus)){
|
||||
usb_standard_request();
|
||||
}else{
|
||||
}
|
||||
}else{ }
|
||||
break;
|
||||
case REQ_TYPE_CLASS:
|
||||
usb_class_request(setup_packet, ep0databuf, ep0dbuflen);
|
||||
@@ -173,9 +172,14 @@ static void EP0_Handler(){
|
||||
*/
|
||||
void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){
|
||||
if(size > endpoints[number].txbufsz) size = endpoints[number].txbufsz;
|
||||
#ifndef USB32
|
||||
uint16_t N2 = (size + 1) >> 1;
|
||||
// the buffer is 16-bit, so we should copy data as it would be uint16_t
|
||||
uint16_t *buf16 = (uint16_t *)buf;
|
||||
#else
|
||||
int N4 = (size + 3) >> 2;
|
||||
uint32_t *buf32 = (uint32_t *)buf;
|
||||
#endif
|
||||
#if defined USB1_16
|
||||
// very bad: what if `size` is odd?
|
||||
uint32_t *out = (uint32_t *)endpoints[number].tx_buf;
|
||||
@@ -184,13 +188,19 @@ void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){
|
||||
}
|
||||
#elif defined USB2_16
|
||||
// use memcpy instead?
|
||||
for(int i = 0; i < N2; i++){
|
||||
for(int i = 0; i < N2; ++i){
|
||||
endpoints[number].tx_buf[i] = buf16[i];
|
||||
}
|
||||
#elif defined USB32
|
||||
for(int i = 0; i < N4; ++i) endpoints[number].tx_buf[i] = buf32[i];
|
||||
#else
|
||||
#error "Define USB1_16 or USB2_16"
|
||||
#error "Define USB1_16 / USB2_16 / USB32"
|
||||
#endif
|
||||
#ifndef USB32
|
||||
USB_BTABLE->EP[number].USB_COUNT_TX = size;
|
||||
#else
|
||||
USB_BTABLE->EP[number].USB_ADDR_COUNT_TX = (USB_BTABLE->EP[number].USB_ADDR_COUNT_TX & 0xffff) | (size << 16);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,8 +235,12 @@ int EP_Read(uint8_t number, uint8_t *buf){
|
||||
// use memcpy instead?
|
||||
for(int i = 0; i < sz; ++i)
|
||||
buf[i] = endpoints[number].rx_buf[i];
|
||||
#elif defined USB32
|
||||
uint32_t *u32buf = (uint32_t*) buf;
|
||||
int N4 = (sz + 3) >> 2;
|
||||
for(int i = 0; i < N4; ++i) u32buf[i] = endpoints[number].rx_buf[i];
|
||||
#else
|
||||
#error "Define USB1_16 or USB2_16"
|
||||
#error "Define USB1_16 / USB2_16 / USB32"
|
||||
#endif
|
||||
return sz;
|
||||
}
|
||||
@@ -243,11 +257,16 @@ static uint16_t lastaddr = LASTADDR_DEFAULT;
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)(ep_t ep)){
|
||||
#ifdef STM32G0
|
||||
// in STM32G0 all buffers should be aligned by 32 bits
|
||||
if(txsz & 3) txsz = ((txsz >> 2)+1) << 2;
|
||||
if(rxsz & 3) rxsz = ((rxsz >> 2)+1) << 2;
|
||||
#endif
|
||||
if(number >= STM32ENDPOINTS) return 4; // out of configured amount
|
||||
if(txsz > USB_BTABLE_SIZE/ACCESSZ || rxsz > USB_BTABLE_SIZE/ACCESSZ) return 1; // buffer too large
|
||||
if(lastaddr + txsz + rxsz >= USB_BTABLE_SIZE/ACCESSZ) return 2; // out of btable
|
||||
USB->EPnR[number] = (type << 9) | (number & USB_EPnR_EA);
|
||||
USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX_1;
|
||||
USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX;
|
||||
if(rxsz & 1) return 3; // wrong rx buffer size
|
||||
uint16_t countrx = 0;
|
||||
if(rxsz < 64) countrx = rxsz / 2;
|
||||
@@ -255,15 +274,28 @@ int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*f
|
||||
if(rxsz & 0x1f) return 3; // should be multiple of 32
|
||||
countrx = 31 + rxsz / 32;
|
||||
}
|
||||
USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr;
|
||||
#ifdef USB32
|
||||
endpoints[number].tx_buf = (uint32_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
|
||||
#else
|
||||
endpoints[number].tx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
|
||||
#endif
|
||||
endpoints[number].txbufsz = txsz;
|
||||
lastaddr += txsz;
|
||||
#ifdef USB32
|
||||
USB_BTABLE->EP[number].USB_ADDR_COUNT_TX = (uint32_t) lastaddr;
|
||||
#else
|
||||
USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr;
|
||||
USB_BTABLE->EP[number].USB_COUNT_TX = 0;
|
||||
USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr;
|
||||
#endif
|
||||
lastaddr += txsz;
|
||||
#ifdef USB32
|
||||
endpoints[number].rx_buf = (uint32_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
|
||||
USB_BTABLE->EP[number].USB_ADDR_COUNT_RX = (uint32_t) lastaddr | countrx << 26;
|
||||
#else
|
||||
endpoints[number].rx_buf = (uint8_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
|
||||
lastaddr += rxsz;
|
||||
USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr;
|
||||
USB_BTABLE->EP[number].USB_COUNT_RX = countrx << 10;
|
||||
#endif
|
||||
lastaddr += rxsz;
|
||||
endpoints[number].func = func;
|
||||
return 0;
|
||||
}
|
||||
@@ -272,7 +304,8 @@ int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*f
|
||||
void USB_IRQ(){
|
||||
uint32_t CNTR = USB->CNTR;
|
||||
USB->CNTR = 0;
|
||||
if(USB->ISTR & USB_ISTR_RESET){
|
||||
uint32_t istr = USB->ISTR;
|
||||
if(istr & USB_ISTR_RESET){
|
||||
usbON = 0;
|
||||
// Reinit registers
|
||||
CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_SUSPM;
|
||||
@@ -281,42 +314,56 @@ void USB_IRQ(){
|
||||
lastaddr = LASTADDR_DEFAULT;
|
||||
// clear address, leave only enable bit
|
||||
USB->DADDR = USB_DADDR_EF;
|
||||
USB->ISTR = ~USB_ISTR_RESET;
|
||||
//USB->ISTR = ~(USB_ISTR_RESET); // clear all flags
|
||||
if(EP_Init(0, EP_TYPE_CONTROL, USB_EP0BUFSZ, USB_EP0BUFSZ, EP0_Handler)){
|
||||
return;
|
||||
};
|
||||
}
|
||||
if(USB->ISTR & USB_ISTR_CTR){
|
||||
if(istr & USB_ISTR_CTR){
|
||||
// EP number
|
||||
uint8_t n = USB->ISTR & USB_ISTR_EPID;
|
||||
uint8_t n = istr & USB_ISTR_EPID;
|
||||
if (istr & USB_ISTR_DIR){ // OUT
|
||||
}else{ // IN
|
||||
}
|
||||
// copy received bytes amount
|
||||
endpoints[n].rx_cnt = USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter
|
||||
endpoints[n].rx_cnt =
|
||||
#ifdef USB32
|
||||
(USB_BTABLE->EP[n].USB_ADDR_COUNT_RX >> 16) & 0x3FF;
|
||||
#else
|
||||
USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter
|
||||
#endif
|
||||
// call EP handler
|
||||
if(endpoints[n].func) endpoints[n].func();
|
||||
}
|
||||
if(USB->ISTR & USB_ISTR_WKUP){ // wakeup
|
||||
#ifndef STM32F0
|
||||
CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LP_MODE | USB_CNTR_WKUPM); // clear suspend flags
|
||||
#else
|
||||
if(istr & USB_ISTR_WKUP){ // wakeup
|
||||
#if defined STM32F0
|
||||
CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LPMODE | USB_CNTR_WKUPM);
|
||||
#endif
|
||||
USB->ISTR = ~USB_ISTR_WKUP;
|
||||
}
|
||||
if(USB->ISTR & USB_ISTR_SUSP){ // suspend -> still no connection, may sleep
|
||||
usbON = 0;
|
||||
#ifndef STM32F0
|
||||
CNTR |= USB_CNTR_FSUSP | USB_CNTR_LP_MODE | USB_CNTR_WKUPM;
|
||||
#elif defined STM32G0
|
||||
CNTR &= ~(USB_CNTR_SUSPEN | USB_CNTR_PDWN | USB_CNTR_WKUPM);
|
||||
#else
|
||||
CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LP_MODE | USB_CNTR_WKUPM); // clear suspend flags
|
||||
#endif
|
||||
//USB->ISTR = ~USB_ISTR_WKUP;
|
||||
}
|
||||
if(istr & USB_ISTR_SUSP){ // suspend -> still no connection, may sleep
|
||||
usbON = 0;
|
||||
#if defined STM32F0
|
||||
CNTR |= USB_CNTR_FSUSP | USB_CNTR_LPMODE | USB_CNTR_WKUPM;
|
||||
#elif defined STM32G0
|
||||
CNTR |= USB_CNTR_SUSPEN | USB_CNTR_WKUPM;
|
||||
#else
|
||||
CNTR |= USB_CNTR_FSUSP | USB_CNTR_LP_MODE | USB_CNTR_WKUPM;
|
||||
#endif
|
||||
CNTR &= ~(USB_CNTR_SUSPM);
|
||||
USB->ISTR = ~USB_ISTR_SUSP;
|
||||
//USB->ISTR = ~USB_ISTR_SUSP;
|
||||
}
|
||||
USB->ISTR = 0; // clear all flags
|
||||
USB->CNTR = CNTR; // rewoke interrupts
|
||||
}
|
||||
|
||||
// here we suppose that all PIN settings done in hw_setup earlier
|
||||
void USB_setup(){
|
||||
lastaddr = LASTADDR_DEFAULT; // clear last address settings
|
||||
#if defined STM32F3
|
||||
NVIC_DisableIRQ(USB_LP_IRQn);
|
||||
// remap USB LP & Wakeup interrupts to 75 and 76 - works only on pure F303
|
||||
@@ -326,6 +373,7 @@ void USB_setup(){
|
||||
NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
|
||||
NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
|
||||
#elif defined STM32F0
|
||||
// All is clocking from HSI48
|
||||
NVIC_DisableIRQ(USB_IRQn);
|
||||
RCC->APB1ENR |= RCC_APB1ENR_CRSEN;
|
||||
RCC->CFGR3 &= ~RCC_CFGR3_USBSW; // reset USB
|
||||
@@ -338,16 +386,33 @@ void USB_setup(){
|
||||
CRS->CR |= CRS_CR_AUTOTRIMEN; // enable auto trim
|
||||
CRS->CR |= CRS_CR_CEN; // enable freq counter & block CRS->CFGR as read-only
|
||||
RCC->CFGR |= RCC_CFGR_SW;
|
||||
#elif defined STM32G0
|
||||
NVIC_DisableIRQ(USB_UCPD1_2_IRQn);
|
||||
PWR->CR2 |= PWR_CR2_USV; // enable USB powering
|
||||
//RCC->APBENR2 |= RCC_APBENR2_SYSCFGEN; // enable tacting of SYSCFG
|
||||
// independent clocking of USB from HSI48
|
||||
RCC->CR |= RCC_CR_HSI48ON;
|
||||
uint32_t tmout = 16000000;
|
||||
while(!(RCC->CR & RCC_CR_HSI48RDY)) if(--tmout == 0){ break;}
|
||||
RCC->CCIPR2 &= ~RCC_CCIPR2_USBSEL; // select HSI48 for USB
|
||||
RCC->APBENR1 |= RCC_APBENR1_CRSEN; // CRS clocking
|
||||
CRS->CFGR = (31LL << CRS_CFGR_FELIM_Pos) | // tolerance (usually 31)
|
||||
(48000LL / 1LL - 1LL) << CRS_CFGR_RELOAD_Pos | // 48MHz / 1kHZ (SOF)
|
||||
CRS_CFGR_SYNCSRC_1; // USB SOF as sync source (0x2)
|
||||
CRS->CR |= CRS_CR_AUTOTRIMEN | CRS_CR_CEN; // Enable autotrim and turn on Clock Recovery System
|
||||
RCC->APBENR1 |= RCC_APBENR1_USBEN;
|
||||
#endif
|
||||
#ifndef STM32G0
|
||||
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
|
||||
//??
|
||||
USB->CNTR = USB_CNTR_FRES; // Force USB Reset
|
||||
for(uint32_t ctr = 0; ctr < 72000; ++ctr) nop(); // wait >1ms
|
||||
USB->CNTR = 0;
|
||||
USB->BTABLE = 0;
|
||||
#else
|
||||
USB->CNTR = USB_CNTR_USBRST;
|
||||
#endif
|
||||
for(uint32_t ctr = 0; ctr < 72000; ++ctr) nop(); // wait >1ms
|
||||
USB->CNTR = USB_CNTR_RESETM; // allow only reset interrupts
|
||||
USB->DADDR = 0;
|
||||
USB->ISTR = 0;
|
||||
USB->CNTR = USB_CNTR_RESETM; // allow only reset interrupts
|
||||
#if defined STM32F3
|
||||
NVIC_EnableIRQ(USB_LP_IRQn);
|
||||
#elif defined STM32F1
|
||||
@@ -355,6 +420,9 @@ void USB_setup(){
|
||||
#elif defined STM32F0
|
||||
USB->BCDR |= USB_BCDR_DPPU;
|
||||
NVIC_EnableIRQ(USB_IRQn);
|
||||
#elif defined STM32G0
|
||||
USB->BCDR |= USB_BCDR_DPPU; // turn ON DP pullup
|
||||
NVIC_EnableIRQ(USB_UCPD1_2_IRQn);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -365,4 +433,6 @@ void usb_lp_isr() __attribute__ ((alias ("USB_IRQ")));
|
||||
void usb_lp_can_rx0_isr() __attribute__ ((alias ("USB_IRQ")));
|
||||
#elif defined STM32F0
|
||||
void usb_isr() __attribute__ ((alias ("USB_IRQ")));
|
||||
#elif defined STM32G0
|
||||
void usb_ucpd1_2_isr() __attribute__ ((alias ("USB_IRQ")));
|
||||
#endif
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
#include <stdint.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#ifndef _U_
|
||||
#define _U_ __attribute__((unused))
|
||||
#endif
|
||||
|
||||
/******************************************************************
|
||||
* Hardware registers etc *
|
||||
*****************************************************************/
|
||||
@@ -30,6 +34,8 @@
|
||||
#define USB_BASE ((uint32_t)0x40005C00)
|
||||
#elif defined STM32F3
|
||||
#include <stm32f3.h>
|
||||
#elif defined STM32G0
|
||||
#include <stm32g0.h>
|
||||
#endif
|
||||
|
||||
// max endpoints number
|
||||
@@ -38,14 +44,16 @@
|
||||
* Buffers size definition
|
||||
**/
|
||||
|
||||
// F0 - USB2_16; F1 - USB1_16; F3 - 1/2 depending on series
|
||||
#if !defined USB1_16 && !defined USB2_16
|
||||
// F0 - USB2_16; F1 - USB1_16; F3 - 1/2 depending on series; G0 - USB32
|
||||
#if !defined USB1_16 && !defined USB2_16 && !defined USB32
|
||||
#if defined STM32F0
|
||||
#define USB2_16
|
||||
#elif defined STM32F1
|
||||
#define USB1_16
|
||||
#elif defined STM32G0
|
||||
#define USB32
|
||||
#else
|
||||
#error "Can't determine USB1_16 or USB2_16, define by hands"
|
||||
#error "Can't determine USB1_16/USB2_16/USB32, define by hands"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -59,8 +67,8 @@
|
||||
#if defined STM32F0
|
||||
#define USB_BTABLE_SIZE 1024
|
||||
#elif defined STM32F3
|
||||
#define USB_BTABLE_SIZE 512
|
||||
#warning "Please, check real buffer size due to docs"
|
||||
#define USB_BTABLE_SIZE 1024
|
||||
//#warning "Please, check real buffer size due to docs"
|
||||
#else
|
||||
#error "define STM32F0 or STM32F3"
|
||||
#endif
|
||||
@@ -68,16 +76,21 @@
|
||||
#if defined STM32F0
|
||||
#define USB_BTABLE_SIZE 768
|
||||
#elif defined STM32F3
|
||||
#define USB_BTABLE_SIZE 512
|
||||
#warning "Please, check real buffer size due to docs"
|
||||
#define USB_BTABLE_SIZE 768
|
||||
#elif defined STM32G0
|
||||
#define USB_BTABLE_SIZE 2048
|
||||
//#warning "Please, check real buffer size due to docs"
|
||||
#else // STM32F103: 1024 bytes but with 32-bit addressing
|
||||
#define USB_BTABLE_SIZE 1024
|
||||
#endif
|
||||
#endif // NOCAN
|
||||
|
||||
// first 64 bytes of USB_BTABLE are registers!
|
||||
|
||||
#ifndef STM32G0
|
||||
#define USB_BTABLE_BASE 0x40006000
|
||||
#else
|
||||
#define USB_BTABLE_BASE 0x40009800
|
||||
#endif
|
||||
#define USB ((USB_TypeDef *) USB_BASE)
|
||||
|
||||
#ifdef USB_BTABLE
|
||||
@@ -120,8 +133,12 @@ typedef struct {
|
||||
__IO uint32_t ISTR;
|
||||
__IO uint32_t FNR;
|
||||
__IO uint32_t DADDR;
|
||||
#ifndef USB32
|
||||
__IO uint32_t BTABLE;
|
||||
#ifdef STM32F0
|
||||
#else
|
||||
__IO uint32_t RESERVED1; // there's no BTABLE register in STM32G0
|
||||
#endif
|
||||
#if defined STM32F0 || defined USB32
|
||||
__IO uint32_t LPMCSR;
|
||||
__IO uint32_t BCDR;
|
||||
#endif
|
||||
@@ -135,16 +152,19 @@ typedef struct{
|
||||
__IO uint16_t USB_ADDR_RX;
|
||||
__IO uint16_t USB_COUNT_RX;
|
||||
#define ACCESSZ (1)
|
||||
#define BUFTYPE uint8_t
|
||||
#elif defined USB1_16
|
||||
__IO uint32_t USB_ADDR_TX;
|
||||
__IO uint32_t USB_COUNT_TX;
|
||||
__IO uint32_t USB_ADDR_RX;
|
||||
__IO uint32_t USB_COUNT_RX;
|
||||
#define ACCESSZ (2)
|
||||
#define BUFTYPE uint16_t
|
||||
#elif defined USB32
|
||||
// 32-bit registers: addr & count in one!
|
||||
__IO uint32_t USB_ADDR_COUNT_TX;
|
||||
__IO uint32_t USB_ADDR_COUNT_RX;
|
||||
#define ACCESSZ (1)
|
||||
#else
|
||||
#error "Define USB1_16 or USB2_16"
|
||||
#error "Define USB1_16 (16 bits over 32bit register), USB2_16 (16 bits over 16 bit register) or USB32 (32 bist over 32 bit register)"
|
||||
#endif
|
||||
} USB_EPDATA_TypeDef;
|
||||
|
||||
@@ -303,9 +323,17 @@ typedef struct {
|
||||
|
||||
// endpoints state
|
||||
typedef struct{
|
||||
#ifdef USB32
|
||||
uint32_t *tx_buf; // transmission buffer address
|
||||
#else
|
||||
uint16_t *tx_buf; // transmission buffer address
|
||||
#endif
|
||||
uint16_t txbufsz; // transmission buffer size
|
||||
#ifdef USB32
|
||||
uint32_t *rx_buf; // reception buffer address
|
||||
#else
|
||||
uint8_t *rx_buf; // reception buffer address
|
||||
#endif
|
||||
void (*func)(); // endpoint action function
|
||||
unsigned rx_cnt : 10; // received data counter
|
||||
} ep_t;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#define BUILD_NUMBER "114"
|
||||
#define BUILD_DATE "2025-03-12"
|
||||
#define BUILD_NUMBER "133"
|
||||
#define BUILD_DATE "2026-04-14"
|
||||
|
||||
@@ -37,7 +37,7 @@ void adc_setup(){
|
||||
ADC1->SMPR2 = ADC_SMPR2_SMP0;
|
||||
ADC1->SMPR1 = ADC_SMPR1_SMP16 | ADC_SMPR1_SMP17;
|
||||
// sequence order: 1[0]->3[1]->14[2]->15[3]->10[4]->11[5] -> 16[tsen] -> 17[vdd]
|
||||
ADC1->SQR3 = (1 << 0) | (3<<5) | (14 << 10) | (15 << 15) | (10 << 20) | (11 < 25);
|
||||
ADC1->SQR3 = (1 << 0) | (3<<5) | (14 << 10) | (15 << 15) | (10 << 20) | (11 << 25);
|
||||
ADC1->SQR2 = (12 << 0) | (13 << 5) | (16 << 10) | (17 << 15);
|
||||
ADC1->SQR1 = (ADC_CHANNELS - 1) << 20; // amount of conversions
|
||||
ADC1->CR1 = ADC_CR1_SCAN; // scan mode
|
||||
|
||||
@@ -8,3 +8,5 @@ LDLIBS := -lm
|
||||
|
||||
include ../makefile.f3
|
||||
include ../../makefile.stm32
|
||||
|
||||
$(OBJDIR)/commproto.o: commproto.cpp $(VERSION_FILE)
|
||||
|
||||
@@ -16,17 +16,26 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifdef EBUG
|
||||
#include "strfunc.h"
|
||||
#endif
|
||||
|
||||
#include "adc.h"
|
||||
|
||||
/**
|
||||
* @brief ADCx_array - arrays for ADC channels with median filtering:
|
||||
* ADC1:
|
||||
* 0 - Ch0 - ADC1_IN1
|
||||
* 1 - Ch1 - ADC1_IN2
|
||||
* 2 - internal Tsens - ADC1_IN16
|
||||
* 3 - Vref - ADC1_IN18
|
||||
* 0 - Ch0 - ADC1_IN1 - NTC1
|
||||
* 1 - Ch1 - ADC1_IN2 - NTC2
|
||||
* 2 - Ch2 - ADC1_IN3 - NTC3
|
||||
* 3 - Ch3 - ADC1_IN4 - NTC4
|
||||
* 4 - internal Tsens - ADC1_IN16
|
||||
* 5 - Vref - ADC1_IN18
|
||||
* ADC2:
|
||||
* 4 - AIN5/DAC_OUT1 - PA4 - DAC1_OUT1 (onboard heater?), PA5 - ADC2_IN2 (DAC output control)
|
||||
* AIN5/DAC_OUT1 - PA4 - DAC1_OUT1 (onboard heater)
|
||||
* 6 - PA5 - ADC2_IN2 (DAC output control)
|
||||
*/
|
||||
static uint16_t ADC_array[NUMBER_OF_ADC_CHANNELS*9];
|
||||
|
||||
@@ -65,17 +74,19 @@ TRUE_INLINE void enADC(ADC_TypeDef *chnl){
|
||||
* ADC1 - DMA1_ch1
|
||||
* ADC2 - DMA2_ch1
|
||||
*/
|
||||
// Setup ADC and DAC
|
||||
// Setup ADC and DAC; ADC/DAC pins should be prepared in gpio_setup
|
||||
void adc_setup(){
|
||||
RCC->AHBENR |= RCC_AHBENR_ADC12EN; // Enable clocking
|
||||
ADC12_COMMON->CCR = ADC_CCR_TSEN | ADC_CCR_VREFEN | ADC_CCR_CKMODE; // enable Tsens and Vref, HCLK/4
|
||||
calADC(ADC1);
|
||||
calADC(ADC2);
|
||||
// ADC1: channels 1,2,16,18; ADC2: channel 2
|
||||
ADC1->SMPR1 = ADC_SMPR1_SMP0 | ADC_SMPR1_SMP1;
|
||||
// ADC1: channels 1,2,3,4,16,18
|
||||
ADC1->SMPR1 = ADC_SMPR1_SMP0 | ADC_SMPR1_SMP1 | ADC_SMPR1_SMP2 | ADC_SMPR1_SMP3;
|
||||
ADC1->SMPR2 = ADC_SMPR2_SMP15 | ADC_SMPR2_SMP17;
|
||||
// 4 conversions in group: 1->2->16->18
|
||||
ADC1->SQR1 = (1<<6) | (2<<12) | (16<<18) | (18<<24) | (NUMBER_OF_ADC1_CHANNELS-1);
|
||||
// 6 conversions in group: 1->2->3->4->16->18
|
||||
ADC1->SQR1 = (1<<6) | (2<<12) | (3<<18) | (4<<24) | (NUMBER_OF_ADC1_CHANNELS-1);
|
||||
ADC1->SQR2 = (16<<0) | (18<<6);
|
||||
// ADC2: channel 2
|
||||
ADC2->SMPR1 = ADC_SMPR1_SMP1;
|
||||
ADC2->SQR1 = (2<<6) | (NUMBER_OF_ADC2_CHANNELS-1);
|
||||
// configure DMA for ADC
|
||||
@@ -109,7 +120,8 @@ void adc_setup(){
|
||||
* @param nch - number of channel
|
||||
* @return
|
||||
*/
|
||||
uint16_t getADCval(int nch){
|
||||
uint16_t getADCval(uint8_t nch){
|
||||
if(nch >= NUMBER_OF_ADC_CHANNELS) return 0;
|
||||
register uint16_t temp;
|
||||
#define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); }
|
||||
#define PIX_SWAP(a,b) { temp=(a);(a)=(b);(b)=temp; }
|
||||
@@ -131,7 +143,7 @@ uint16_t getADCval(int nch){
|
||||
}
|
||||
|
||||
// get voltage @input nch (V)
|
||||
float getADCvoltage(int nch){
|
||||
float getADCvoltage(uint8_t nch){
|
||||
float v = getADCval(nch);
|
||||
v *= getVdd();
|
||||
v /= 4096.f; // 12bit ADC
|
||||
@@ -155,3 +167,114 @@ float getVdd(){
|
||||
vdd /= getADCval(ADC_VREF);
|
||||
return vdd;
|
||||
}
|
||||
|
||||
// R lookup table for T=-10..59 degreesC
|
||||
#if 0
|
||||
T=[-10:59]+273.15;
|
||||
R=1000*exp(3950*(1./T-1/298.15));
|
||||
for i=1:length(T); printf("\t%.1f,\t// %d \n", R(i), T(i)-273.15); endfor
|
||||
#endif
|
||||
|
||||
static const float Rlut[] = {
|
||||
5824.6, // -10
|
||||
5502.8, // -9
|
||||
5201.1, // -8
|
||||
4917.9, // -7
|
||||
4652.2, // -6
|
||||
4402.6, // -5
|
||||
4168.1, // -4
|
||||
3947.7, // -3
|
||||
3740.5, // -2
|
||||
3545.5, // -1
|
||||
3362.1, // 0
|
||||
3189.3, // 1
|
||||
3026.6, // 2
|
||||
2873.3, // 3
|
||||
2728.8, // 4
|
||||
2592.5, // 5
|
||||
2463.9, // 6
|
||||
2342.5, // 7
|
||||
2227.9, // 8
|
||||
2119.7, // 9
|
||||
2017.5, // 10
|
||||
1920.8, // 11
|
||||
1829.4, // 12
|
||||
1743.0, // 13
|
||||
1661.2, // 14
|
||||
1583.7, // 15
|
||||
1510.4, // 16
|
||||
1440.9, // 17
|
||||
1375.1, // 18
|
||||
1312.7, // 19
|
||||
1253.5, // 20
|
||||
1197.4, // 21
|
||||
1144.1, // 22
|
||||
1093.6, // 23
|
||||
1045.6, // 24
|
||||
1000.0, // 25
|
||||
956.7, // 26
|
||||
915.5, // 27
|
||||
876.4, // 28
|
||||
839.1, // 29
|
||||
803.7, // 30
|
||||
770.0, // 31
|
||||
737.9, // 32
|
||||
707.4, // 33
|
||||
678.3, // 34
|
||||
650.6, // 35
|
||||
624.1, // 36
|
||||
598.9, // 37
|
||||
574.9, // 38
|
||||
552.0, // 39
|
||||
530.1, // 40
|
||||
509.3, // 41
|
||||
489.4, // 42
|
||||
470.3, // 43
|
||||
452.2, // 44
|
||||
434.8, // 45
|
||||
418.2, // 46
|
||||
402.4, // 47
|
||||
387.2, // 48
|
||||
372.7, // 49
|
||||
358.8, // 50
|
||||
345.5, // 51
|
||||
332.8, // 52
|
||||
320.7, // 53
|
||||
309.0, // 54
|
||||
297.8, // 55
|
||||
287.1, // 56
|
||||
276.9, // 57
|
||||
267.1, // 58
|
||||
257.7, // 59
|
||||
};
|
||||
|
||||
#define LUTSZ (sizeof(Rlut) / sizeof(float))
|
||||
|
||||
/**
|
||||
* @brief getNTCtemp - stupid LUT-search and linear approximation of T by R
|
||||
* @param nch - channel of ADC for Tx
|
||||
* @return temperature in degr.C
|
||||
*/
|
||||
float getNTCtemp(uint8_t nch){
|
||||
if(nch > ADC_AIN4) return -300.f; // bad number
|
||||
uint16_t val = getADCval(nch);
|
||||
if(val < 5) return -400.f; // short cirquit
|
||||
else if(val > 4090) return -500.f; // no NTC
|
||||
float R = 1000.f / (4096.f / val - 1.f); // resistance of NTC
|
||||
#ifdef EBUG
|
||||
USB_sendstr("R="); USB_sendstr(float2str(R, 1)); newline();
|
||||
#endif
|
||||
int left = 0, right = LUTSZ-1;
|
||||
if(R > Rlut[0]) right = 1;
|
||||
else if(R < Rlut[LUTSZ-1]) left = LUTSZ-2;
|
||||
while(right - left > 1){
|
||||
int idx = left + (right - left) / 2;
|
||||
float Rl = Rlut[idx];
|
||||
if(Rl > R) left = idx + 1;
|
||||
else right = idx - 1;
|
||||
}
|
||||
if(left >= (int)LUTSZ) return 60.f;
|
||||
float Rleft = Rlut[left], Rright = Rlut[left+1];
|
||||
float T = (float)left - 9.f - (R - Rright) / (Rleft - Rright);
|
||||
return T;
|
||||
}
|
||||
|
||||
@@ -19,22 +19,27 @@
|
||||
#pragma once
|
||||
#include <stm32f3.h>
|
||||
|
||||
#define NUMBER_OF_ADC1_CHANNELS (4)
|
||||
// 4 sensors on 1..4, TS (16) and Vdd (18)
|
||||
#define NUMBER_OF_ADC1_CHANNELS (6)
|
||||
#define NUMBER_OF_ADC2_CHANNELS (1)
|
||||
// total number of channels - for array
|
||||
#define NUMBER_OF_ADC_CHANNELS ((NUMBER_OF_ADC1_CHANNELS+NUMBER_OF_ADC2_CHANNELS))
|
||||
|
||||
// channels of ADC in array
|
||||
#define ADC_AIN0 (0)
|
||||
#define ADC_AIN1 (1)
|
||||
#define ADC_TS (2)
|
||||
#define ADC_VREF (3)
|
||||
#define ADC_AIN5 (4)
|
||||
#define ADC_AIN1 (0)
|
||||
#define ADC_AIN2 (1)
|
||||
#define ADC_AIN3 (2)
|
||||
#define ADC_AIN4 (3)
|
||||
#define ADC_NTCIN(x) ((x)-1)
|
||||
#define ADC_TS (4)
|
||||
#define ADC_VREF (5)
|
||||
#define ADC_DACIN (6)
|
||||
// starting index of ADC2
|
||||
#define ADC2START (9*NUMBER_OF_ADC1_CHANNELS)
|
||||
|
||||
void adc_setup();
|
||||
float getMCUtemp();
|
||||
float getVdd();
|
||||
uint16_t getADCval(int nch);
|
||||
float getADCvoltage(int nch);
|
||||
uint16_t getADCval(uint8_t nch);
|
||||
float getADCvoltage(uint8_t nch);
|
||||
float getNTCtemp(uint8_t nch);
|
||||
|
||||
Binary file not shown.
627
F3:F303/MLX90640-allsky/commproto.cpp
Normal file
627
F3:F303/MLX90640-allsky/commproto.cpp
Normal file
@@ -0,0 +1,627 @@
|
||||
#include <cstring>
|
||||
|
||||
extern "C"{
|
||||
#include <stm32f3.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "adc.h"
|
||||
#include "commproto.h"
|
||||
#include "hardware.h"
|
||||
#include "i2c.h"
|
||||
#include "mlxproc.h"
|
||||
#include "strfunc.h"
|
||||
#include "usart.h"
|
||||
#include "usb_dev.h"
|
||||
}
|
||||
|
||||
// image aquisition time
|
||||
const char* const Timage = "TIMAGE";
|
||||
|
||||
// Global senders to send info into other interface
|
||||
static int (*usb_sender)(const char*) = nullptr;
|
||||
static int (*usart_sender)(const char*) = nullptr;
|
||||
static int (*usb_putb)(uint8_t) = nullptr;
|
||||
static int (*usart_putb)(uint8_t) = nullptr;
|
||||
static int (*usb_sendbin)(const uint8_t*, int) = nullptr;
|
||||
static int (*usart_sendbin)(const uint8_t*, int) = nullptr;
|
||||
|
||||
// Function helpers
|
||||
static int (*SEND)(const char*) = nullptr;
|
||||
static int (*putb)(uint8_t) = nullptr;
|
||||
static int (*sendbin)(const uint8_t*, int) = nullptr;
|
||||
|
||||
#define N() putb('\n')
|
||||
#define printu(x) SEND(u2str(x))
|
||||
#define printi(x) SEND(i2str(x))
|
||||
#define printuhex(x) SEND(uhex2str(x))
|
||||
#define printfl(x,n) SEND(float2str(x, n))
|
||||
|
||||
void set_senders(int (*usbs)(const char *),
|
||||
int (*usbb)(uint8_t),
|
||||
int (*usbbin)(const uint8_t *, int),
|
||||
int (*usarts)(const char *),
|
||||
int (*usartb)(uint8_t),
|
||||
int (*usartbin)(const uint8_t *, int)){
|
||||
usb_sender = usbs;
|
||||
usb_putb = usbb;
|
||||
usb_sendbin = usbbin;
|
||||
usart_sender = usarts;
|
||||
usart_putb = usartb;
|
||||
usart_sendbin = usartbin;
|
||||
}
|
||||
|
||||
// Local buffer for I2C data
|
||||
#define LOCBUFFSZ 32
|
||||
static uint16_t locBuffer[LOCBUFFSZ];
|
||||
// default I2C address of sensor
|
||||
static uint8_t I2Caddress = 0x33 << 1;
|
||||
extern volatile uint32_t Tms;
|
||||
// show `cartoon` - continuously draw ASCII image of current sensor
|
||||
uint8_t cartoon = 0;
|
||||
|
||||
// Command list
|
||||
#define COMMAND_TABLE \
|
||||
COMMAND(help, "show this help") \
|
||||
COMMAND(ascii, "draw nth image in ASCII (n=0..4)") \
|
||||
COMMAND(binary, "get nth image as text array of floats") \
|
||||
COMMAND(listids, "list active sensors IDs") \
|
||||
COMMAND(tempmap, "show temperature map of nth image") \
|
||||
COMMAND(acqtime, "show nth image aquisition time") \
|
||||
COMMAND(bmereinit, "reinit BME280") \
|
||||
COMMAND(environ, "get environment parameters") \
|
||||
COMMAND(state, "get MLX state") \
|
||||
COMMAND(reset, "reset MCU") \
|
||||
COMMAND(time, "print current Tms") \
|
||||
COMMAND(iicaddr, "get/set I2C address (non-shifted)") \
|
||||
COMMAND(mlxcont, "continue MLX") \
|
||||
COMMAND(iicspeed, "get/set I2C speed (0..4)") \
|
||||
COMMAND(mlxpause, "pause MLX") \
|
||||
COMMAND(mlxstop, "stop MLX") \
|
||||
COMMAND(adc, "get n'th ADC values") \
|
||||
COMMAND(ntc, "get n'th NTC temperatures") \
|
||||
COMMAND(cartoon, "toggle cartoon mode") \
|
||||
COMMAND(mlxdump, "dump MLX parameters for sensor n") \
|
||||
COMMAND(mlxaddr, "get/set MLX address of sensor n") \
|
||||
COMMAND(readreg, "read I2C register: readreg reg [= nwords]") \
|
||||
COMMAND(writedata, "write I2C data: writedata = val1 val2 ...") \
|
||||
COMMAND(iicscan, "scan I2C bus") \
|
||||
COMMAND(mcutemp, "get MCU temperature") \
|
||||
COMMAND(mcuvdd, "get MCU Vdd") \
|
||||
COMMAND(dac, "get/set DAC value") \
|
||||
COMMAND(pwm, "get/set PWM for channel n (0..100%)") \
|
||||
COMMAND(sendstr, "send string to other interface: sendstr = text")
|
||||
|
||||
|
||||
// Command prototypes
|
||||
#define COMMAND(name, desc) static errcodes_t cmd_ ## name(const char*, char*);
|
||||
COMMAND_TABLE
|
||||
#undef COMMAND
|
||||
|
||||
// descrtiptions for `help`
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
} CmdInfo;
|
||||
|
||||
static const CmdInfo cmdInfo[] = {
|
||||
#define COMMAND(name, desc) { #name, desc },
|
||||
COMMAND_TABLE
|
||||
#undef COMMAND
|
||||
};
|
||||
|
||||
// Text descriptions for error codes
|
||||
static const char* errtxt[ERR_AMOUNT] = {
|
||||
[ERR_OK] = "OK\n",
|
||||
[ERR_BADCMD] = "BADCMD\n",
|
||||
[ERR_BADPAR] = "BADPAR\n",
|
||||
[ERR_BADVAL] = "BADVAL\n",
|
||||
[ERR_WRONGLEN] = "WRONGLEN\n",
|
||||
[ERR_CANTRUN] = "CANTRUN\n",
|
||||
[ERR_BUSY] = "BUSY\n",
|
||||
[ERR_OVERFLOW] = "OVERFLOW\n",
|
||||
};
|
||||
|
||||
const char *EQ = " = "; // equal sign for getters
|
||||
|
||||
// send `command = `
|
||||
#define CMDEQ() do{SEND(cmd); SEND(EQ);}while(0)
|
||||
// send `commandXXX = `
|
||||
#define CMDEQP(x) do{SEND(cmd); SEND(u2str((uint32_t)x)); SEND(EQ);}while(0)
|
||||
|
||||
/**
|
||||
* @brief splitargs - get command parameter and setter from `args`
|
||||
* @param args (i) - rest of string after command (like `1 = PU OD OUT`)
|
||||
* @param parno (o) - parameter number or -1 if none
|
||||
* @return setter (part after `=` without leading spaces) or NULL if none
|
||||
*/
|
||||
static const char *splitargs(char *args, int32_t *parno){
|
||||
if(!args) return NULL;
|
||||
uint32_t U32;
|
||||
const char *next = getnum(args, &U32);
|
||||
int p = -1;
|
||||
if(next != args && U32 <= MAXPARNO) p = U32;
|
||||
if(parno) *parno = p;
|
||||
next = strchr(next, '=');
|
||||
if(next){
|
||||
if(*(++next)) next = omit_spaces(next);
|
||||
if(*next == 0) next = NULL;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief argsvals - split `args` into `parno` and setter's value
|
||||
* @param args - rest of string after command
|
||||
* @param parno (o) - parameter number or -1 if none
|
||||
* @param parval - integer setter's value
|
||||
* @return false if no setter or it's not a number, true - got setter's num
|
||||
*/
|
||||
static bool argsvals(char *args, int32_t *parno, int32_t *parval){
|
||||
const char *setter = splitargs(args, parno);
|
||||
if(!setter) return false;
|
||||
int32_t I32;
|
||||
const char *next = getint(setter, &I32);
|
||||
if(next != setter && parval){
|
||||
*parval = I32;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/************* List of proto functions for each command *************/
|
||||
|
||||
static errcodes_t cmd_help(const char*, char*){
|
||||
SEND(REPOURL);
|
||||
for(size_t i = 0; i < sizeof(cmdInfo)/sizeof(cmdInfo[0]); ++i){
|
||||
SEND(cmdInfo[i].name);
|
||||
SEND(" - ");
|
||||
SEND(cmdInfo[i].desc);
|
||||
SEND("\n");
|
||||
}
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_time(const char* cmd, char*){
|
||||
CMDEQ();
|
||||
printu(Tms); N();
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_reset(const char*, char*){
|
||||
NVIC_SystemReset();
|
||||
return ERR_CANTRUN; // unreacheable
|
||||
}
|
||||
|
||||
// send acquisition time
|
||||
static void imaqtime(uint8_t sensno){
|
||||
uint32_t T = mlx_lastimT(sensno);
|
||||
SEND(Timage); printu(sensno); SEND(EQ);
|
||||
printu(T); N();
|
||||
}
|
||||
|
||||
// Common image command for ASCII/binary/tempmap
|
||||
static errcodes_t image_cmd(char* args, int mode){
|
||||
int32_t sensno = -1;
|
||||
splitargs(args, &sensno);
|
||||
if(sensno < 0 || sensno >= N_SENSORS) return ERR_BADPAR;
|
||||
fp_t *img = mlx_getimage(sensno);
|
||||
if(!img) return ERR_CANTRUN;
|
||||
|
||||
// Frame number
|
||||
imaqtime(sensno);
|
||||
|
||||
switch(mode){
|
||||
case 0: // tempmap
|
||||
dumpIma(img);
|
||||
break;
|
||||
case 1: // ascii
|
||||
drawIma(img);
|
||||
break;
|
||||
case 2: // binary
|
||||
SEND("BINARY"); putb('0'+sensno); putb('=');
|
||||
uint8_t *d = (uint8_t*)img;
|
||||
uint32_t _2send = MLX_PIXNO * sizeof(float);
|
||||
// send by portions of 256 bytes
|
||||
while(_2send){
|
||||
uint32_t portion = (_2send > 256) ? 256 : _2send;
|
||||
if(sendbin(d, portion)){
|
||||
_2send -= portion;
|
||||
d += portion;
|
||||
}
|
||||
}
|
||||
SEND("ENDIMAGE"); N();
|
||||
break;
|
||||
}
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_ascii(const char* , char* args){
|
||||
return image_cmd(args, 1);
|
||||
}
|
||||
static errcodes_t cmd_binary(const char* , char* args){
|
||||
return image_cmd(args, 2);
|
||||
}
|
||||
static errcodes_t cmd_tempmap(const char* , char* args){
|
||||
return image_cmd(args, 0);
|
||||
}
|
||||
|
||||
static errcodes_t cmd_acqtime(const char* , char* args){
|
||||
int32_t sensno = -1;
|
||||
splitargs(args, &sensno);
|
||||
if(sensno < 0 || sensno >= N_SENSORS) return ERR_BADPAR;
|
||||
imaqtime(sensno);
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_listids(const char*, char*){
|
||||
int N = mlx_nactive();
|
||||
if(!N) return ERR_CANTRUN;
|
||||
uint8_t *ids = mlx_activeids();
|
||||
SEND("Found "); printu(N); SEND(" active sensors:\n");
|
||||
for(int i = 0; i < N_SENSORS; ++i){
|
||||
if(ids[i]){
|
||||
SEND("SENSID"); printu(i); SEND(EQ); printuhex(ids[i]>>1); N();
|
||||
}
|
||||
}
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_bmereinit(const char*, char*){
|
||||
if(bme_init()) return ERR_OK;
|
||||
return ERR_CANTRUN;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_environ(const char*, char*){
|
||||
bme280_t env;
|
||||
if(!get_environment(&env)) return ERR_CANTRUN;
|
||||
SEND("TEMPERATURE="); printfl(env.T, 2); N();
|
||||
SEND("SKYTEMPERATURE="); printfl(env.Tsky, 2); N();
|
||||
SEND("PRESSURE_HPA="); printfl(env.P/100.f, 2); N();
|
||||
SEND("PRESSURE_MM="); printfl(env.P * 0.00750062f, 2); N();
|
||||
SEND("HUMIDITY="); printfl(env.H, 2); N();
|
||||
SEND("TEMP_DEW="); printfl(env.Tdew, 1); N();
|
||||
SEND("T_MEASUREMENT="); printu(env.Tmeas); N();
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_state(const char* cmd, char*){
|
||||
static const char *states[] = {
|
||||
[MLX_NOTINIT] = "not init",
|
||||
[MLX_WAITPARAMS] = "wait parameters DMA read",
|
||||
[MLX_WAITSUBPAGE] = "wait subpage",
|
||||
[MLX_READSUBPAGE] = "wait subpage DMA read",
|
||||
[MLX_RELAX] = "do nothing"
|
||||
};
|
||||
mlx_state_t s = mlx_state();
|
||||
CMDEQ(); SEND(states[s]); N();
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
/********** I2C commands **********/
|
||||
|
||||
static errcodes_t cmd_iicaddr(const char* cmd, char* args){
|
||||
int32_t addr;
|
||||
if(argsvals(args, NULL, &addr)){
|
||||
if(addr < 0 || addr > 0x7f) return ERR_BADVAL;
|
||||
I2Caddress = (uint8_t)(addr << 1);
|
||||
mlx_sethwaddr(I2Caddress, addr);
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
// getter
|
||||
CMDEQ(); printuhex(I2Caddress >> 1); N();
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_mlxcont(const char*, char*){
|
||||
mlx_continue();
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_iicspeed(const char* cmd, char* args){
|
||||
static const char *speeds[] = {"10K","100K","400K","1M","2M"};
|
||||
int32_t speed;
|
||||
// TODO: allow string parameter
|
||||
if(argsvals(args, NULL, &speed)){
|
||||
if (speed < 0 || speed >= I2C_SPEED_AMOUNT) return ERR_BADVAL;
|
||||
i2c_setup((i2c_speed_t)speed);
|
||||
}
|
||||
// getter
|
||||
CMDEQ(); SEND(speeds[i2c_curspeed]); N();
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_mlxpause(const char*, char*){
|
||||
mlx_pause();
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_mlxstop(const char*, char*){
|
||||
mlx_stop();
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_adc(const char* cmd, char* args){
|
||||
if(!args){ // show all values
|
||||
for(uint8_t i = 0; i < NUMBER_OF_ADC_CHANNELS; ++i){
|
||||
CMDEQP(i); printu(getADCval(i)); N();
|
||||
}
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
int32_t addr;
|
||||
splitargs(args, &addr);
|
||||
if(addr < 0 || addr >= NUMBER_OF_ADC_CHANNELS) return ERR_BADPAR;
|
||||
CMDEQP(addr); printu(getADCval(static_cast<uint8_t>(addr))); N();
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_ntc(const char* cmd, char* args){
|
||||
if(!args){ // show all values
|
||||
for(uint8_t i = 0; i <= ADC_AIN4; ++i){
|
||||
CMDEQP(i); printfl(getNTCtemp(i), 1); N();
|
||||
}
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
int32_t addr;
|
||||
splitargs(args, &addr);
|
||||
if(addr < 0 || addr > ADC_AIN4) return ERR_BADPAR;
|
||||
CMDEQP(addr); printfl(getNTCtemp(static_cast<uint8_t>(addr)), 1); N();
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_cartoon(const char*, char*){
|
||||
// TODO: should be getter/setter!
|
||||
cartoon = !cartoon;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_mlxdump(const char*, char* args){
|
||||
int32_t sensno = -1;
|
||||
splitargs(args, &sensno);
|
||||
if (sensno < 0 || sensno >= N_SENSORS) return ERR_BADPAR;
|
||||
MLX90640_params *params = mlx_getparams(sensno);
|
||||
if(!params) return ERR_CANTRUN;
|
||||
|
||||
SEND("SENSNO="); printi(sensno); N();
|
||||
SEND("kVdd="); printi(params->kVdd); N();
|
||||
SEND("vdd25="); printi(params->vdd25); N();
|
||||
SEND("KvPTAT="); printfl(params->KvPTAT, 4); N();
|
||||
SEND("KtPTAT="); printfl(params->KtPTAT, 4); N();
|
||||
SEND("vPTAT25="); printi(params->vPTAT25); N();
|
||||
SEND("alphaPTAT="); printfl(params->alphaPTAT, 2); N();
|
||||
SEND("gainEE="); printi(params->gainEE); N();
|
||||
SEND("Pixel offset parameters:\n");
|
||||
dumpIma(params->offset);
|
||||
SEND("K_talpha:\n");
|
||||
dumpIma(params->kta);
|
||||
SEND("Kv: ");
|
||||
for(int i = 0; i < 4; ++i) { printfl(params->kv[i], 2); putb(' '); }
|
||||
N();
|
||||
SEND("cpOffset="); printi(params->cpOffset[0]); SEND(", "); printi(params->cpOffset[1]); N();
|
||||
SEND("cpKta="); printfl(params->cpKta, 2); N();
|
||||
SEND("cpKv="); printfl(params->cpKv, 2); N();
|
||||
SEND("tgc="); printfl(params->tgc, 2); N();
|
||||
SEND("cpALpha="); printfl(params->cpAlpha[0], 2); SEND(", "); printfl(params->cpAlpha[1], 2); N();
|
||||
SEND("KsTa="); printfl(params->KsTa, 2); N();
|
||||
SEND("Alpha:\n");
|
||||
dumpIma(params->alpha);
|
||||
SEND("CT3="); printfl(params->CT[1], 2); N();
|
||||
SEND("CT4="); printfl(params->CT[2], 2); N();
|
||||
for(int i = 0; i < 4; ++i){
|
||||
SEND("KsTo"); putb('0'+i); putb('='); printfl(params->KsTo[i], 2); N();
|
||||
SEND("alphacorr"); putb('0'+i); putb('='); printfl(params->alphacorr[i], 2); N();
|
||||
}
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_mlxaddr(const char* cmd, char* args){
|
||||
int32_t sensno = -1;
|
||||
if(!args || !*args) { // without args: show global address
|
||||
//CMDEQ(); printuhex(I2Caddress>>1); N();
|
||||
//return ERR_AMOUNT;
|
||||
return ERR_BADPAR;
|
||||
}
|
||||
const char *setter = splitargs(args, &sensno);
|
||||
if(sensno < 0 || sensno >= N_SENSORS) return ERR_BADPAR;
|
||||
|
||||
if(setter){ // setter: set current address
|
||||
uint32_t a;
|
||||
const char *nxt = getnum(setter, &a);
|
||||
if(nxt == setter || a > 0x7f) return ERR_BADVAL;
|
||||
mlx_setaddr(sensno, (uint8_t)a);
|
||||
return ERR_AMOUNT;
|
||||
}else{ // getter
|
||||
uint8_t a = mlx_getaddr(sensno);
|
||||
CMDEQP(sensno); printuhex(a); N();
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
}
|
||||
|
||||
static errcodes_t cmd_readreg(const char* cmd, char* args){
|
||||
int32_t reg = -1, nwords = 1;
|
||||
const char *setter = splitargs(args, ®);
|
||||
if(reg < 0) return ERR_BADPAR;
|
||||
if(setter){ // read more than one byte
|
||||
uint32_t n;
|
||||
const char *nxt = getnum(setter, &n);
|
||||
if (nxt == setter || n == 0 || n > I2C_BUFSIZE) return ERR_BADVAL;
|
||||
nwords = (int32_t)n;
|
||||
}
|
||||
uint16_t *data = i2c_read_reg16(I2Caddress, (uint16_t)reg, nwords, 0);
|
||||
if(!data) return ERR_CANTRUN;
|
||||
if(nwords == 1){
|
||||
CMDEQP(reg); printuhex(*data); N();
|
||||
}else{
|
||||
CMDEQP(reg); N(); hexdump16(SEND, data, nwords);
|
||||
}
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_writedata(const char*, char* args){
|
||||
const char *setter = splitargs(args, NULL);
|
||||
if(!setter) return ERR_BADVAL;
|
||||
int N = 0;
|
||||
const char *p = setter;
|
||||
while (*p){
|
||||
if(N >= LOCBUFFSZ) return ERR_AMOUNT;
|
||||
uint32_t val;
|
||||
p = getnum(p, &val);
|
||||
if(p == setter) break; // not a number
|
||||
locBuffer[N++] = (uint16_t)val;
|
||||
p = omit_spaces(p);
|
||||
if (!*p) break;
|
||||
}
|
||||
if(N == 0) return ERR_BADVAL;
|
||||
if(!i2c_write(I2Caddress, locBuffer, N)) return ERR_CANTRUN;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_iicscan(const char*, char*) {
|
||||
i2c_init_scan_mode();
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_mcutemp(const char* cmd, char*){
|
||||
CMDEQ(); printfl(getMCUtemp(), 2); N();
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_mcuvdd(const char* cmd, char*){
|
||||
CMDEQ(); printfl(getVdd(), 2); N();
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_dac(const char* cmd, char* args){
|
||||
int32_t val;
|
||||
if(argsvals(args, NULL, &val)){
|
||||
if(val < 0 || val > 4095) return ERR_BADVAL;
|
||||
DAC1->DHR12R1 = static_cast<uint32_t>(val);
|
||||
}
|
||||
// getter
|
||||
CMDEQ(); printu(DAC1->DHR12R1); N();
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static void showpwm(const char* cmd, uint8_t nch){
|
||||
uint16_t ccr;
|
||||
switch(nch){
|
||||
case 0: ccr = TIM3->CCR1; break;
|
||||
case 1: ccr = TIM3->CCR2; break;
|
||||
case 2: ccr = TIM3->CCR3; break;
|
||||
case 3: ccr = TIM3->CCR4; break;
|
||||
default: return;
|
||||
}
|
||||
CMDEQP(nch); printu(ccr); N();
|
||||
}
|
||||
|
||||
// four PWM channels: 1,2 - heaters, 3,4 - info
|
||||
static errcodes_t cmd_pwm(const char* cmd, char* args){
|
||||
int32_t ch = -1, val;
|
||||
const char *setter = splitargs(args, &ch);
|
||||
if(ch < 0 || ch > PWM_CH_MAX){ // all channels
|
||||
for(uint8_t i = 0; i <= PWM_CH_MAX; ++i)
|
||||
showpwm(cmd, i);
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
if(setter){
|
||||
if(!getint(setter, &val) || val < 0 || val > 100) return ERR_BADVAL;
|
||||
if(!setPWM(static_cast<uint8_t>(ch), static_cast<uint32_t>(val)))
|
||||
return ERR_CANTRUN;
|
||||
}
|
||||
// getter
|
||||
showpwm(cmd, (uint8_t)ch);
|
||||
return ERR_AMOUNT;
|
||||
}
|
||||
|
||||
static errcodes_t cmd_sendstr(const char*, char* args) {
|
||||
int32_t dummy;
|
||||
const char *text = splitargs(args, &dummy);
|
||||
if(!text || !*text) return ERR_BADVAL;
|
||||
// switch to other interface
|
||||
int (*other_sender)(const char*) = (SEND == usb_sender) ? usart_sender : usb_sender;
|
||||
if (!other_sender) return ERR_CANTRUN;
|
||||
other_sender(text);
|
||||
other_sender("\n");
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static constexpr uint32_t hash(const char* str, uint32_t h = 0) {
|
||||
return *str ? hash(str + 1, h + ((h << 7) ^ *str)) : h;
|
||||
}
|
||||
|
||||
|
||||
const char *parse_cmd(int (*sendfun)(const char*), char *buf) {
|
||||
if(!buf || !*buf || !sendfun) return NULL;
|
||||
SEND = sendfun;
|
||||
if(sendfun == usb_sender){
|
||||
putb = usb_putb;
|
||||
sendbin = usb_sendbin;
|
||||
}else{
|
||||
putb = usart_putb;
|
||||
sendbin = usart_sendbin;
|
||||
}
|
||||
char command[CMD_MAXLEN+1];
|
||||
int i = 0;
|
||||
while(*buf > '@' && i < CMD_MAXLEN) command[i++] = *buf++;
|
||||
command[i] = 0;
|
||||
while(*buf && *buf <= ' ') ++buf;
|
||||
char *args = buf;
|
||||
#ifdef EBUG
|
||||
USB_sendstr("__args='"); USB_sendstr(args); USB_sendstr("'\n");
|
||||
#endif
|
||||
if(!*args) args = NULL;
|
||||
|
||||
uint32_t h = hash(command);
|
||||
errcodes_t ecode = ERR_AMOUNT;
|
||||
switch (h){
|
||||
#define COMMAND(name, desc) case hash(#name): ecode = cmd_##name(command, args); break;
|
||||
COMMAND_TABLE
|
||||
#undef COMMAND
|
||||
default:
|
||||
SEND("Unknown command, try 'help'\n");
|
||||
}
|
||||
if(ecode < ERR_AMOUNT) return errtxt[ecode];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dumpIma(const fp_t im[MLX_PIXNO]){
|
||||
for(int row = 0; row < MLX_H; ++row){
|
||||
for(int col = 0; col < MLX_W; ++col){
|
||||
printfl(*im++, 1);
|
||||
putb(' ');
|
||||
}
|
||||
N();
|
||||
}
|
||||
}
|
||||
|
||||
#define GRAY_LEVELS 16
|
||||
static const char *const CHARS_16 = " .':;+*oxX#&%B$@";
|
||||
void drawIma(const fp_t im[MLX_PIXNO]){
|
||||
fp_t min_val = im[0], max_val = im[0];
|
||||
const fp_t *iptr = im;
|
||||
for(int row = 0; row < MLX_H; ++row)
|
||||
for(int col = 0; col < MLX_W; ++col){
|
||||
fp_t cur = *iptr++;
|
||||
if (cur < min_val) min_val = cur;
|
||||
else if (cur > max_val) max_val = cur;
|
||||
}
|
||||
fp_t range = max_val - min_val;
|
||||
SEND("RANGE="); printfl(range, 3); N();
|
||||
SEND("MIN="); printfl(min_val, 3); N();
|
||||
SEND("MAX="); printfl(max_val, 3); N();
|
||||
if(fabsf(range) < 0.001) range = 1.0f;
|
||||
iptr = im;
|
||||
char string[MLX_W+2];
|
||||
string[MLX_W] = '\n'; string[MLX_W+1] = 0; // end of line
|
||||
for(int row = 0; row < MLX_H; ++row){
|
||||
for(int col = 0; col < MLX_W; ++col){
|
||||
fp_t normalized = ((*iptr++) - min_val) / range;
|
||||
int idx = (int)(normalized * GRAY_LEVELS);
|
||||
if(idx < 0) idx = 0;
|
||||
else if(idx >= GRAY_LEVELS) idx = GRAY_LEVELS-1;
|
||||
string[col] = CHARS_16[idx];
|
||||
}
|
||||
SEND(string);
|
||||
}
|
||||
N();
|
||||
}
|
||||
61
F3:F303/MLX90640-allsky/commproto.h
Normal file
61
F3:F303/MLX90640-allsky/commproto.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of the as3935 project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mlx90640.h"
|
||||
|
||||
#include "version.inc"
|
||||
|
||||
#ifdef EBUG
|
||||
#define RLSDBG "debug"
|
||||
#else
|
||||
#define RLSDBG "release"
|
||||
#endif
|
||||
|
||||
#define REPOURL "https://github.com/eddyem/stm32samples/tree/master/F3:F303/MLX90640-allsky " RLSDBG " build #" BUILD_NUMBER "@" BUILD_DATE "\n"
|
||||
|
||||
|
||||
// error codes for answer message
|
||||
typedef enum{
|
||||
ERR_OK, // all OK
|
||||
ERR_BADCMD, // wrong command
|
||||
ERR_BADPAR, // wrong parameter
|
||||
ERR_BADVAL, // wrong value (for setter)
|
||||
ERR_WRONGLEN, // wrong message length
|
||||
ERR_CANTRUN, // can't run given command due to bad parameters or other
|
||||
ERR_BUSY, // target interface busy, try later
|
||||
ERR_OVERFLOW, // string was too long -> overflow
|
||||
ERR_AMOUNT // amount of error codes or "send nothing"
|
||||
} errcodes_t;
|
||||
|
||||
// maximal length of command (without trailing zero)
|
||||
#define CMD_MAXLEN 15
|
||||
// maximal available parameter number (for 16-bit registers is 0xffff
|
||||
#define MAXPARNO 0xffff
|
||||
|
||||
extern const char *EQ;
|
||||
const char *parse_cmd(int (*sendfun)(const char*), char *buf);
|
||||
void set_senders(int (*usbs)(const char*), int (*usbb)(uint8_t), int (*usbbin)(const uint8_t*, int),
|
||||
int (*usarts)(const char*), int (*usartb)(uint8_t), int (*usartbin)(const uint8_t*, int));
|
||||
|
||||
extern const char *const Timage;
|
||||
|
||||
extern uint8_t cartoon;
|
||||
void dumpIma(const fp_t im[MLX_PIXNO]);
|
||||
void drawIma(const fp_t im[MLX_PIXNO]);
|
||||
@@ -51,16 +51,19 @@ TRUE_INLINE void iwdg_setup(){
|
||||
TRUE_INLINE void gpio_setup(){
|
||||
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN; // for USART and LEDs
|
||||
for(int i = 0; i < 10000; ++i) nop();
|
||||
// USB - alternate function 14 @ pins PA11/PA12; SWD - AF0 @PA13/14; USB pullup - PA15
|
||||
// PA6 - PWM for external heater (TIM3_CH1 or TIM16_CH1); PA7 - PWM propto (humidity - 50%)
|
||||
// USB - alternate function 14 @ pins PA11/PA12; SWD - AF0 @PA13/14
|
||||
// PA6,PA7 - PWM for external heaters (TIM3_CH1, TIM3_CH2)
|
||||
// PA0..PA4 - NTC in, PA5 - DAC_OUT1 (board heater), PA6 - ADC in for DAC out
|
||||
// USART pins will be setup in usart.c
|
||||
GPIOA->AFR[0] = AFRf(2, 6) | AFRf(2, 7);
|
||||
GPIOA->AFR[1] = AFRf(14, 11) | AFRf(14, 12);
|
||||
GPIOA->MODER = MODER_AI(0) | MODER_AI(1) | MODER_AI(4) | MODER_AI(5) | MODER_AF(6) |
|
||||
MODER_AF(7) | MODER_AF(11) | MODER_AF(12) | MODER_AF(13) | MODER_AF(14) | MODER_O(15);
|
||||
// PB0 - PWM propto Text (<=20 - 0%, >=30 - 100%), PB1 - PWM propto (Text-Tsky) (<=-5 - 0%, >=+35 - 100%) PB2 - SPI_CS
|
||||
// force USB DP to low level for a while
|
||||
GPIOA->MODER = MODER_AI(0) | MODER_AI(1) | MODER_AI(2) | MODER_AI(3) | MODER_AI(4) | MODER_AI(5) | MODER_AF(6) |
|
||||
MODER_AF(7) | MODER_AF(11) | MODER_O(12) | MODER_AF(13) | MODER_AF(14) | MODER_O(15);
|
||||
// PB0 - PWM propto Text (<=20 - 0%, >=30 - 100%), PB1 - PWM propto (Text-Tsky) (<=-5 - 0%, >=+35 - 100%) PB9 - SPI_CS
|
||||
// SPI and I2C will be setup in spi.c and i2c.c
|
||||
GPIOB->AFR[0] = AFRf(2, 0) | AFRf(2, 1);
|
||||
GPIOB->MODER = MODER_AF(0) | MODER_AF(1) | MODER_O(2);
|
||||
pin_set(GPIOB, 1<<1);
|
||||
GPIOB->MODER = MODER_AF(0) | MODER_AF(1) | MODER_O(9);
|
||||
SPI_CS_1();
|
||||
}
|
||||
|
||||
@@ -85,7 +88,7 @@ TRUE_INLINE void pwm_setup(){
|
||||
|
||||
// change PWM value in percents; return 0 if `val` is bad or `ch` not 0..3
|
||||
int setPWM(uint8_t ch, uint8_t val){
|
||||
if(ch > 3 || val > PWM_CCR_MAX) return 0;
|
||||
if(ch >= PWM_CH_MAX || val > PWM_CCR_MAX) return 0;
|
||||
volatile uint32_t *CCRs = &(TIM3->CCR1);
|
||||
CCRs[ch] = val;
|
||||
return 1;
|
||||
@@ -139,7 +142,6 @@ void bme_process(){
|
||||
// set PWM duty propto humidity
|
||||
float h = (Humidity - 50.f) * 2.f;
|
||||
if(h < 0.f) h = 0.f; else if(h > 100.f) h = 100.f;
|
||||
setPWM(PWM_CH_HUMIDITY, (uint8_t)h);
|
||||
environment.Tmeas = Tms;
|
||||
// set PWM duty propto external T
|
||||
float t = (Temperature + 20.f) * 2.f;
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
#define USBPU_ON() pin_clear(USBPU_port, USBPU_pin)
|
||||
#define USBPU_OFF() pin_set(USBPU_port, USBPU_pin)
|
||||
|
||||
// SPI_CS - PB2
|
||||
#define SPI_CS_1() pin_set(GPIOB, 1<<2)
|
||||
#define SPI_CS_0() pin_clear(GPIOB, 1<<2)
|
||||
// SPI_CS - PB9
|
||||
#define SPI_CS_1() pin_set(GPIOB, 1<<9)
|
||||
#define SPI_CS_0() pin_clear(GPIOB, 1<<9)
|
||||
|
||||
// interval of environment measurements, ms
|
||||
#define ENV_MEAS_PERIOD (10000)
|
||||
@@ -36,14 +36,11 @@
|
||||
// Max PWM CCR1 value (->1)
|
||||
#define PWM_CCR_MAX (100)
|
||||
// PWM channels (start from 0 - CH1)
|
||||
// external heater
|
||||
#define PWM_CH_HEATER (0)
|
||||
// propto humidity (the higher - the brighter)
|
||||
#define PWM_CH_HUMIDITY (1)
|
||||
// propto external T (the higher - the brighter)
|
||||
#define PWM_CH_TEXT (2)
|
||||
// propto Tsky - Text (the higher - the brighter)
|
||||
#define PWM_CH_TSKY (3)
|
||||
#define PWM_CH_MAX (3)
|
||||
|
||||
typedef struct{
|
||||
float T; // temperature, degC
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 17.0.1, 2025-10-06T23:24:05. -->
|
||||
<!-- Written by QtCreator 19.0.1, 2026-05-06T23:28:28. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
@@ -86,6 +86,7 @@
|
||||
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
||||
</valuemap>
|
||||
<value type="int" key="RcSync">0</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
@@ -153,6 +154,7 @@
|
||||
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
@@ -162,6 +164,7 @@
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
</valuemap>
|
||||
@@ -185,6 +188,7 @@
|
||||
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
@@ -194,6 +198,7 @@
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
</valuemap>
|
||||
@@ -204,10 +209,6 @@
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="qlonglong">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>Version</variable>
|
||||
<value type="int">22</value>
|
||||
|
||||
@@ -2,6 +2,8 @@ BMP280.c
|
||||
BMP280.h
|
||||
adc.c
|
||||
adc.h
|
||||
commproto.cpp
|
||||
commproto.h
|
||||
hardware.c
|
||||
hardware.h
|
||||
i2c.c
|
||||
@@ -15,8 +17,6 @@ mlx90640.h
|
||||
mlx90640_regs.h
|
||||
mlxproc.c
|
||||
mlxproc.h
|
||||
proto.c
|
||||
proto.h
|
||||
ringbuffer.c
|
||||
ringbuffer.h
|
||||
spi.c
|
||||
|
||||
|
Before Width: | Height: | Size: 344 B After Width: | Height: | Size: 354 B |
@@ -20,7 +20,7 @@
|
||||
#include "hardware.h"
|
||||
#include "i2c.h"
|
||||
#include "mlxproc.h"
|
||||
#include "proto.h"
|
||||
#include "commproto.h"
|
||||
#include "strfunc.h"
|
||||
#include "usart.h"
|
||||
#include "usb_dev.h"
|
||||
@@ -43,14 +43,18 @@ int main(void){
|
||||
StartHSI();
|
||||
SysTick_Config((uint32_t)48000); // 1ms
|
||||
}
|
||||
USBPU_OFF();
|
||||
USBPU_OFF(); // for development board with managed pullup resistor
|
||||
hw_setup();
|
||||
adc_setup();
|
||||
i2c_setup(I2C_SPEED_400K);
|
||||
bme_init();
|
||||
USB_setup();
|
||||
usart_setup(115200);
|
||||
// setup USB DP as alternate function - for sensors' board with constant pullup resistor
|
||||
GPIOA->MODER = (GPIOA->MODER & ~GPIO_MODER_MODER12) | MODER_AF(12);
|
||||
USBPU_ON();
|
||||
USB_setup();
|
||||
// set senders for abiliby of sending messages between interfaces
|
||||
set_senders(USB_sendstr, USB_putbyte, USB_send, usart_sendstr, usart_putbyte, usart_send);
|
||||
uint32_t ctr = Tms, Tlastima[N_SENSORS] = {0};
|
||||
mlx_continue(); // init state machine
|
||||
while(1){
|
||||
@@ -63,7 +67,7 @@ int main(void){
|
||||
int l = USB_receivestr(inbuff, MAXSTRLEN);
|
||||
if(l < 0) USB_sendstr("USBOVERFLOW\n");
|
||||
else if(l){
|
||||
const char *ans = parse_cmd(inbuff, SEND_USB);
|
||||
const char *ans = parse_cmd(USB_sendstr, inbuff);
|
||||
if(ans) USB_sendstr(ans);
|
||||
}
|
||||
if(i2c_scanmode){ // send this to both
|
||||
@@ -84,7 +88,6 @@ int main(void){
|
||||
if(Tnow != Tlastima[i]){
|
||||
fp_t *im = mlx_getimage(i);
|
||||
if(im){
|
||||
chsendfun(SEND_USB);
|
||||
//U(Sensno); UN(i2str(i));
|
||||
U(Timage); USB_putbyte('0'+i); USB_putbyte('='); UN(u2str(Tnow));
|
||||
drawIma(im);
|
||||
@@ -96,7 +99,7 @@ int main(void){
|
||||
if(usart_ovr()) usart_sendstr("USART_OVERFLOW\n");
|
||||
char *got = usart_getline(NULL);
|
||||
if(got){
|
||||
const char *ans = parse_cmd(got, SEND_USART);
|
||||
const char *ans = parse_cmd(usart_sendstr, got);
|
||||
if(ans) usart_sendstr(ans);
|
||||
}
|
||||
bme_process();
|
||||
|
||||
@@ -63,12 +63,16 @@ static int sensno = -1;
|
||||
mlx_state_t mlx_state(){ return MLX_state; }
|
||||
// set address
|
||||
int mlx_setaddr(int n, uint8_t addr){
|
||||
if(n < 0 || n > N_SENSORS) return 0;
|
||||
if(n < 0 || n >= N_SENSORS) return 0;
|
||||
if(addr > 0x7f) return 0;
|
||||
sens_addresses[n] = addr << 1;
|
||||
Tlastimage[n] = Tms; // refresh counter for autoreset I2C in case of error
|
||||
return 1;
|
||||
}
|
||||
uint8_t mlx_getaddr(int n){
|
||||
if(n < 0 || n >= N_SENSORS) return 0;
|
||||
return sens_addresses[n];
|
||||
}
|
||||
// pause state machine and stop
|
||||
void mlx_pause(){
|
||||
MLX_oldstate = MLX_state;
|
||||
|
||||
@@ -39,6 +39,7 @@ typedef enum{
|
||||
} mlx_state_t;
|
||||
|
||||
int mlx_setaddr(int n, uint8_t addr);
|
||||
uint8_t mlx_getaddr(int n);
|
||||
mlx_state_t mlx_state();
|
||||
int mlx_nactive();
|
||||
uint8_t *mlx_activeids();
|
||||
|
||||
@@ -1,539 +0,0 @@
|
||||
/*
|
||||
* This file is part of the ir-allsky project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stm32f3.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "adc.h"
|
||||
#include "hardware.h"
|
||||
#include "i2c.h"
|
||||
#include "mlxproc.h"
|
||||
#include "proto.h"
|
||||
#include "strfunc.h"
|
||||
#include "usart.h"
|
||||
#include "usb_dev.h"
|
||||
#include "version.inc"
|
||||
|
||||
#define LOCBUFFSZ (32)
|
||||
// local buffer for I2C data to send
|
||||
static uint16_t locBuffer[LOCBUFFSZ];
|
||||
static uint8_t I2Caddress = 0x33 << 1;
|
||||
extern volatile uint32_t Tms;
|
||||
uint8_t cartoon = 0; // "cartoon" mode: refresh image each time we get new
|
||||
|
||||
// functions to send data over USB or USART: to change them use flag in `parse_cmd`
|
||||
typedef struct{
|
||||
int (*S)(const char*); // send string
|
||||
int (*P)(uint8_t); // put byte
|
||||
int (*B)(const uint8_t*, int); // send raw bytes
|
||||
} sendfun_t;
|
||||
|
||||
static sendfun_t usbsend = {
|
||||
.S = USB_sendstr, .P = USB_putbyte, .B = USB_send
|
||||
};
|
||||
static sendfun_t usartsend = {
|
||||
.S = usart_sendstr, .P = usart_putbyte, .B = usart_send
|
||||
};
|
||||
|
||||
static sendfun_t *sendfun = &usbsend;
|
||||
|
||||
void chsendfun(int sendto){
|
||||
if(sendto == SEND_USB) sendfun = &usbsend;
|
||||
else sendfun = &usartsend;
|
||||
}
|
||||
|
||||
// newline
|
||||
#define N() sendfun->P('\n')
|
||||
#define printu(x) do{sendfun->S(u2str(x));}while(0)
|
||||
#define printi(x) do{sendfun->S(i2str(x));}while(0)
|
||||
#define printuhex(x) do{sendfun->S(uhex2str(x));}while(0)
|
||||
#define printfl(x,n) do{sendfun->S(float2str(x, n));}while(0)
|
||||
|
||||
// common names for frequent keys
|
||||
const char* const Timage = "TIMAGE";
|
||||
const char* const Image = "IMAGE";
|
||||
static const char *const Sensno = "SENSNO=";
|
||||
|
||||
static const char *const OK = "OK\n", *const ERR = "ERR\n";
|
||||
const char *const helpstring =
|
||||
"https://github.com/eddyem/stm32samples/tree/master/F3:F303/MLX90640multi build#" BUILD_NUMBER " @ " BUILD_DATE "\n"
|
||||
" management of single IR bolometer MLX90640\n"
|
||||
"dn - draw nth image in ASCII\n"
|
||||
"gn - get nth image 'as is' - float array of 768x4 bytes\n"
|
||||
"l - list active sensors IDs\n"
|
||||
"mn - show temperature map of nth image\n"
|
||||
"tn - show nth image aquisition time\n"
|
||||
"B - reinit BME280\n"
|
||||
"E - get environment parameters (temperature etc)\n"
|
||||
"G - get MLX state\n"
|
||||
"R - reset device\n"
|
||||
"T - print current Tms\n"
|
||||
" Debugging options:\n"
|
||||
"aa - change I2C address to a (a should be non-shifted value!!!)\n"
|
||||
"c - continue MLX\n"
|
||||
"i0..4 - setup I2C with speed 10k, 100k, 400k, 1M or 2M (experimental!)\n"
|
||||
"p - pause MLX\n"
|
||||
"s - stop MLX (and start from zero @ 'c')\n"
|
||||
"A - get ADC values\n"
|
||||
"C - \"cartoon\" mode on/off (show each new image) - USB only!!!\n"
|
||||
"Dn - dump MLX parameters for sensor number n\n"
|
||||
"Ia addr [n] - set device address for interactive work or (with n) change address of n'th sensor\n"
|
||||
"Ir reg n - read n words from 16-bit register\n"
|
||||
"Iw words - send words (hex/dec/oct/bin) to I2C\n"
|
||||
"Is - scan I2C bus\n"
|
||||
"M - get MCU temperature and Vdd value\n"
|
||||
"O - set output of DAC (0..4095)\n"
|
||||
"Px - set PWM output (0..100%) or get current value\n"
|
||||
"Us - send string 's' to other interface\n"
|
||||
;
|
||||
|
||||
TRUE_INLINE const char *setupI2C(char *buf){
|
||||
static const char * const speeds[I2C_SPEED_AMOUNT] = {
|
||||
[I2C_SPEED_10K] = "10K",
|
||||
[I2C_SPEED_100K] = "100K",
|
||||
[I2C_SPEED_400K] = "400K",
|
||||
[I2C_SPEED_1M] = "1M",
|
||||
[I2C_SPEED_2M] = "2M"
|
||||
};
|
||||
if(buf && *buf){
|
||||
buf = omit_spaces(buf);
|
||||
int speed = *buf - '0';
|
||||
if(speed < 0 || speed >= I2C_SPEED_AMOUNT){
|
||||
return ERR;
|
||||
}
|
||||
i2c_setup((i2c_speed_t)speed);
|
||||
}
|
||||
sendfun->S("I2CSPEED="); sendfun->S(speeds[i2c_curspeed]); N();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TRUE_INLINE const char *chhwaddr(const char *buf){
|
||||
uint32_t a;
|
||||
if(buf && *buf){
|
||||
const char *nxt = getnum(buf, &a);
|
||||
if(nxt && nxt != buf){
|
||||
if(!mlx_sethwaddr(I2Caddress, a)) return ERR;
|
||||
}else{
|
||||
sendfun->S("Wrong number"); N();
|
||||
return ERR;
|
||||
}
|
||||
}else{
|
||||
sendfun->S("Need address"); N();
|
||||
return ERR;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
// read sensor's number from `buf`; return -1 if error
|
||||
static int getsensnum(const char *buf){
|
||||
if(!buf || !*buf) return -1;
|
||||
uint32_t num;
|
||||
const char *nxt = getnum(buf, &num);
|
||||
if(!nxt || nxt == buf || num >= N_SENSORS) return -1;
|
||||
return (int) num;
|
||||
}
|
||||
|
||||
TRUE_INLINE const char *chaddr(const char *buf){
|
||||
uint32_t addr;
|
||||
const char *nxt = getnum(buf, &addr);
|
||||
if(nxt && nxt != buf){
|
||||
if(addr > 0x7f) return ERR;
|
||||
I2Caddress = (uint8_t) addr << 1;
|
||||
int n = getsensnum(nxt);
|
||||
if(n > -1) mlx_setaddr(n, addr);
|
||||
}else addr = I2Caddress >> 1;
|
||||
sendfun->S("I2CADDR="); sendfun->S(uhex2str(addr)); N();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// read I2C register[s] - only blocking read! (DMA allowable just for config/image reading in main process)
|
||||
static const char *rdI2C(const char *buf){
|
||||
uint32_t N = 0;
|
||||
const char *nxt = getnum(buf, &N);
|
||||
if(!nxt || buf == nxt || N > 0xffff) return ERR;
|
||||
buf = nxt;
|
||||
uint16_t reg = N, *b16 = NULL;
|
||||
nxt = getnum(buf, &N);
|
||||
if(!nxt || buf == nxt || N == 0 || N > I2C_BUFSIZE) return ERR;
|
||||
if(!(b16 = i2c_read_reg16(I2Caddress, reg, N, 0))) return ERR;
|
||||
if(N == 1){
|
||||
char b[5];
|
||||
u16s(*b16, b);
|
||||
b[4] = 0;
|
||||
sendfun->S(b); N();
|
||||
}else hexdump16(sendfun->S, b16, N);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// read N numbers from buf, @return 0 if wrong or none
|
||||
TRUE_INLINE uint16_t readNnumbers(const char *buf){
|
||||
uint32_t D;
|
||||
const char *nxt;
|
||||
uint16_t N = 0;
|
||||
while((nxt = getnum(buf, &D)) && nxt != buf && N < LOCBUFFSZ){
|
||||
buf = nxt;
|
||||
locBuffer[N++] = (uint16_t) D;
|
||||
}
|
||||
return N;
|
||||
}
|
||||
|
||||
static const char *wrI2C(const char *buf){
|
||||
uint16_t N = readNnumbers(buf);
|
||||
if(N == 0) return ERR;
|
||||
for(int i = 0; i < N; ++i){
|
||||
sendfun->S("byte "); sendfun->S(u2str(i));
|
||||
sendfun->S(" :"); sendfun->S(uhex2str(locBuffer[i])); N();
|
||||
}
|
||||
if(!i2c_write(I2Caddress, locBuffer, N)) return ERR;
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void dumpfarr(float *arr){
|
||||
for(int row = 0; row < 24; ++row){
|
||||
for(int col = 0; col < 32; ++col){
|
||||
printfl(*arr++, 2); sendfun->P(' ');
|
||||
}
|
||||
N();
|
||||
}
|
||||
}
|
||||
// dump MLX parameters
|
||||
TRUE_INLINE void dumpparams(const char *buf){
|
||||
int N = getsensnum(buf);
|
||||
if(N < 0){ sendfun->S(ERR); return; }
|
||||
MLX90640_params *params = mlx_getparams(N);
|
||||
if(!params){ sendfun->S(ERR); return; }
|
||||
N(); sendfun->S(Sensno); sendfun->S(i2str(N));
|
||||
sendfun->S("\nkVdd="); printi(params->kVdd);
|
||||
sendfun->S("\nvdd25="); printi(params->vdd25);
|
||||
sendfun->S("\nKvPTAT="); printfl(params->KvPTAT, 4);
|
||||
sendfun->S("\nKtPTAT="); printfl(params->KtPTAT, 4);
|
||||
sendfun->S("\nvPTAT25="); printi(params->vPTAT25);
|
||||
sendfun->S("\nalphaPTAT="); printfl(params->alphaPTAT, 2);
|
||||
sendfun->S("\ngainEE="); printi(params->gainEE);
|
||||
sendfun->S("\nPixel offset parameters:\n");
|
||||
float *offset = params->offset;
|
||||
for(int row = 0; row < 24; ++row){
|
||||
for(int col = 0; col < 32; ++col){
|
||||
printfl(*offset++, 2); sendfun->P(' ');
|
||||
}
|
||||
N();
|
||||
}
|
||||
sendfun->S("K_talpha:\n");
|
||||
dumpfarr(params->kta);
|
||||
sendfun->S("Kv: ");
|
||||
for(int i = 0; i < 4; ++i){
|
||||
printfl(params->kv[i], 2); sendfun->P(' ');
|
||||
}
|
||||
sendfun->S("\ncpOffset=");
|
||||
printi(params->cpOffset[0]); sendfun->S(", "); printi(params->cpOffset[1]);
|
||||
sendfun->S("\ncpKta="); printfl(params->cpKta, 2);
|
||||
sendfun->S("\ncpKv="); printfl(params->cpKv, 2);
|
||||
sendfun->S("\ntgc="); printfl(params->tgc, 2);
|
||||
sendfun->S("\ncpALpha="); printfl(params->cpAlpha[0], 2);
|
||||
sendfun->S(", "); printfl(params->cpAlpha[1], 2);
|
||||
sendfun->S("\nKsTa="); printfl(params->KsTa, 2);
|
||||
sendfun->S("\nAlpha:\n");
|
||||
dumpfarr(params->alpha);
|
||||
sendfun->S("\nCT3="); printfl(params->CT[1], 2);
|
||||
sendfun->S("\nCT4="); printfl(params->CT[2], 2);
|
||||
for(int i = 0; i < 4; ++i){
|
||||
sendfun->S("\nKsTo"); sendfun->P('0'+i); sendfun->P('=');
|
||||
printfl(params->KsTo[i], 2);
|
||||
sendfun->S("\nalphacorr"); sendfun->P('0'+i); sendfun->P('=');
|
||||
printfl(params->alphacorr[i], 2);
|
||||
}
|
||||
N();
|
||||
}
|
||||
// get MLX state
|
||||
TRUE_INLINE void getst(){
|
||||
static const char *states[] = {
|
||||
[MLX_NOTINIT] = "not init",
|
||||
[MLX_WAITPARAMS] = "wait parameters DMA read",
|
||||
[MLX_WAITSUBPAGE] = "wait subpage",
|
||||
[MLX_READSUBPAGE] = "wait subpage DMA read",
|
||||
[MLX_RELAX] = "do nothing"
|
||||
};
|
||||
mlx_state_t s = mlx_state();
|
||||
sendfun->S("MLXSTATE=");
|
||||
sendfun->S(states[s]); N();
|
||||
}
|
||||
|
||||
// `draw`==1 - draw, ==0 - show T map, 2 - send raw float array with prefix 'TIMAGEX=y\nIMAGEX=' and postfix "ENDIMAGE\n"
|
||||
static const char *drawimg(const char *buf, int draw){
|
||||
int sensno = getsensnum(buf);
|
||||
if(sensno > -1){
|
||||
uint32_t T = mlx_lastimT(sensno);
|
||||
fp_t *img = mlx_getimage(sensno);
|
||||
if(img){
|
||||
//sendfun->S(Sensno); sendfun->S(u2str(sensno)); N();
|
||||
sendfun->S(Timage); sendfun->P('0'+sensno); sendfun->P('='); sendfun->S(u2str(T)); N();
|
||||
switch(draw){
|
||||
case 0:
|
||||
dumpIma(img);
|
||||
break;
|
||||
case 1:
|
||||
drawIma(img);
|
||||
break;
|
||||
case 2:
|
||||
sendfun->S(Image); sendfun->P('0'+sensno); sendfun->P('=');
|
||||
uint8_t *d = (uint8_t*)img;
|
||||
uint32_t _2send = MLX_PIXNO * sizeof(float);
|
||||
// send by portions of 256 bytes (as image is larger than ringbuffer)
|
||||
while(_2send){
|
||||
uint32_t portion = (_2send > 256) ? 256 : _2send;
|
||||
sendfun->B(d, portion);
|
||||
_2send -= portion;
|
||||
d += portion;
|
||||
}
|
||||
sendfun->S("ENDIMAGE"); N();
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return ERR;
|
||||
}
|
||||
|
||||
TRUE_INLINE void listactive(){
|
||||
int N = mlx_nactive();
|
||||
if(!N){ sendfun->S("No active sensors found!\n"); return; }
|
||||
uint8_t *ids = mlx_activeids();
|
||||
sendfun->S("Found "); sendfun->P('0'+N);
|
||||
sendfun->S(" active sensors:"); N();
|
||||
for(int i = 0; i < N_SENSORS; ++i)
|
||||
if(ids[i]){
|
||||
sendfun->S("SENSID");
|
||||
sendfun->S(u2str(i)); sendfun->P('=');
|
||||
sendfun->S(uhex2str(ids[i] >> 1));
|
||||
N();
|
||||
}
|
||||
}
|
||||
|
||||
static void getimt(const char *buf){
|
||||
int sensno = getsensnum(buf);
|
||||
if(sensno > -1){
|
||||
sendfun->S(Timage); sendfun->P('0'+sensno); sendfun->P('='); sendfun->S(u2str(mlx_lastimT(sensno))); N();
|
||||
}else sendfun->S(ERR);
|
||||
}
|
||||
|
||||
TRUE_INLINE void getenv(){
|
||||
bme280_t env;
|
||||
if(!get_environment(&env)) sendfun->S("BADENVIRONMENT\n");
|
||||
sendfun->S("TEMPERATURE="); sendfun->S(float2str(env.T, 2));
|
||||
sendfun->S("\nSKYTEMPERATURE="); sendfun->S(float2str(env.Tsky, 2));
|
||||
sendfun->S("\nPRESSURE_HPA="); sendfun->S(float2str(env.P/100.f, 2));
|
||||
sendfun->S("\nPRESSURE_MM="); sendfun->S(float2str(env.P * 0.00750062f, 2));
|
||||
sendfun->S("\nHUMIDITY="); sendfun->S(float2str(env.H, 2));
|
||||
sendfun->S("\nTEMP_DEW="); sendfun->S(float2str(env.Tdew, 1));
|
||||
sendfun->S("\nT_MEASUREMENT="); sendfun->S(u2str(env.Tmeas));
|
||||
N();
|
||||
}
|
||||
|
||||
TRUE_INLINE const char *DAC_chval(const char *buf){
|
||||
uint32_t D;
|
||||
const char *nxt = getnum(buf, &D);
|
||||
if(!nxt || nxt == buf || D > 4095) return ERR;
|
||||
DAC1->DHR12R1 = D;
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
TRUE_INLINE void getADC(){
|
||||
sendfun->S("AIN0="); sendfun->S(u2str(getADCval(ADC_AIN0)));
|
||||
sendfun->S("\nAIN1="); sendfun->S(u2str(getADCval(ADC_AIN1)));
|
||||
sendfun->S("\nAIN5="); sendfun->S(u2str(getADCval(ADC_AIN5)));
|
||||
N();
|
||||
}
|
||||
|
||||
TRUE_INLINE void getMCUvals(){
|
||||
sendfun->S("MCUTEMP="); sendfun->S(float2str(getMCUtemp(), 2));
|
||||
sendfun->S("\nMCUVDD="); sendfun->S(float2str(getVdd(), 2));
|
||||
N();
|
||||
}
|
||||
|
||||
TRUE_INLINE const char* setpwm(const char *buf){
|
||||
uint32_t D;
|
||||
if(!buf || !*buf){
|
||||
sendfun->S("PWM1="); sendfun->S(u2str(TIM3->CCR1));
|
||||
sendfun->S("\nPWM2="); sendfun->S(u2str(TIM3->CCR2));
|
||||
sendfun->S("\nPWM3="); sendfun->S(u2str(TIM3->CCR3));
|
||||
sendfun->S("\nPWM4="); sendfun->S(u2str(TIM3->CCR4));
|
||||
N();
|
||||
return NULL;
|
||||
}
|
||||
const char *nxt = getnum(buf, &D);
|
||||
if(!nxt || nxt == buf || !setPWM(PWM_CH_HEATER, D)) return ERR;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief parse_cmd - user string parser
|
||||
* @param buf - user data
|
||||
* @param isusb - ==1 to send answer over usb, else send over USART1
|
||||
* @return answer OK/ERR or NULL
|
||||
*/
|
||||
const char *parse_cmd(char *buf, int sendto){
|
||||
if(!buf || !*buf) return NULL;
|
||||
chsendfun(sendto);
|
||||
if(buf[1]){
|
||||
switch(*buf++){ // "long" commands
|
||||
case 'a':
|
||||
return chhwaddr(buf);
|
||||
case 'd':
|
||||
return drawimg(buf, 1);
|
||||
case 'g':
|
||||
return drawimg(buf, 2);
|
||||
case 'i':
|
||||
return setupI2C(buf);
|
||||
case 'm':
|
||||
return drawimg(buf, 0);
|
||||
case 't':
|
||||
getimt(buf); return NULL;
|
||||
case 'D':
|
||||
dumpparams(buf);
|
||||
return NULL;
|
||||
break;
|
||||
case 'I':
|
||||
buf = omit_spaces(buf);
|
||||
switch(*buf){
|
||||
case 'a':
|
||||
return chaddr(buf);
|
||||
case 'r':
|
||||
return rdI2C(buf);
|
||||
case 'w':
|
||||
return wrI2C(buf);
|
||||
case 's':
|
||||
i2c_init_scan_mode();
|
||||
return OK;
|
||||
default:
|
||||
return ERR;
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
return DAC_chval(buf);
|
||||
case 'P':
|
||||
return setpwm(buf);
|
||||
case 'U':
|
||||
if(sendto == SEND_USB) chsendfun(SEND_USART);
|
||||
else chsendfun(SEND_USB);
|
||||
if(sendfun->S(buf) && N()) return OK;
|
||||
return ERR;
|
||||
default:
|
||||
return ERR;
|
||||
}
|
||||
}
|
||||
switch(*buf){ // "short" (one letter) commands
|
||||
case 'A':
|
||||
getADC();
|
||||
break;
|
||||
case 'c':
|
||||
mlx_continue(); return OK;
|
||||
break;
|
||||
case 'i': return setupI2C(NULL); // current settings
|
||||
case 'l':
|
||||
listactive();
|
||||
break;
|
||||
case 'p':
|
||||
mlx_pause(); return OK;
|
||||
break;
|
||||
case 's':
|
||||
mlx_stop(); return OK;
|
||||
case 'B':
|
||||
if(bme_init()) return OK;
|
||||
return ERR;
|
||||
case 'C':
|
||||
if(sendto != SEND_USB) return ERR;
|
||||
cartoon = !cartoon; return OK;
|
||||
case 'E':
|
||||
getenv();
|
||||
break;
|
||||
case 'G':
|
||||
getst();
|
||||
break;
|
||||
case 'M':
|
||||
getMCUvals();
|
||||
break;
|
||||
case 'P':
|
||||
return setpwm(NULL);
|
||||
case 'R':
|
||||
NVIC_SystemReset();
|
||||
break;
|
||||
case 'T':
|
||||
sendfun->S("T="); sendfun->S(u2str(Tms)); N();
|
||||
break;
|
||||
case '?': // help
|
||||
case 'h':
|
||||
case 'H':
|
||||
sendfun->S(helpstring);
|
||||
break;
|
||||
default:
|
||||
return ERR;
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// dump image as temperature matrix
|
||||
void dumpIma(const fp_t im[MLX_PIXNO]){
|
||||
for(int row = 0; row < MLX_H; ++row){
|
||||
for(int col = 0; col < MLX_W; ++col){
|
||||
printfl(*im++, 1);
|
||||
sendfun->P(' ');
|
||||
}
|
||||
N();
|
||||
}
|
||||
}
|
||||
|
||||
#define GRAY_LEVELS (16)
|
||||
// 16-level character set ordered by fill percentage (provided by user)
|
||||
static const char *const CHARS_16 = " .':;+*oxX#&%B$@";
|
||||
// draw image in ASCII-art
|
||||
void drawIma(const fp_t im[MLX_PIXNO]){
|
||||
// Find min and max values
|
||||
fp_t min_val = im[0], max_val = im[0];
|
||||
const fp_t *iptr = im;
|
||||
for(int row = 0; row < MLX_H; ++row){
|
||||
for(int col = 0; col < MLX_W; ++col){
|
||||
fp_t cur = *iptr++;
|
||||
if(cur < min_val) min_val = cur;
|
||||
else if(cur > max_val) max_val = cur;
|
||||
}
|
||||
}
|
||||
fp_t range = max_val - min_val;
|
||||
sendfun->S("RANGE="); sendfun->S(float2str(range, 3));
|
||||
sendfun->S("\nMIN="); sendfun->S(float2str(min_val, 3));
|
||||
sendfun->S("\nMAX="); sendfun->S(float2str(max_val, 3)); N();
|
||||
if(fabsf(range) < 0.001) range = 1.; // solid fill -> blank
|
||||
// Generate and print ASCII art
|
||||
iptr = im;
|
||||
for(int row = 0; row < MLX_H; ++row){
|
||||
for(int col = 0; col < MLX_W; ++col){
|
||||
fp_t normalized = ((*iptr++) - min_val) / range;
|
||||
// Map to character index (0 to 15)
|
||||
int index = (int)(normalized * GRAY_LEVELS);
|
||||
// Ensure we stay within bounds
|
||||
if(index < 0) index = 0;
|
||||
else if(index > (GRAY_LEVELS-1)) index = (GRAY_LEVELS-1);
|
||||
sendfun->P(CHARS_16[index]);
|
||||
}
|
||||
N();
|
||||
}
|
||||
N();
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#define BUILD_NUMBER "44"
|
||||
#define BUILD_DATE "2025-10-06"
|
||||
#define BUILD_NUMBER "65"
|
||||
#define BUILD_DATE "2026-05-06"
|
||||
|
||||
Reference in New Issue
Block a user