stm32/subghz: Add STM32WL55 subghz radio interface to stm module.
This is the minimum C interface to allow a modem driver to be built in Python. Interface is simple, with the intention that the micropython-lib driver is the main (only) consumer of it. Signed-off-by: Angus Gratton <angus@redyak.com.au>
This commit is contained in:
parent
9e0f934cdf
commit
02620c2236
@ -102,3 +102,39 @@ the second CPU, the RF core.
|
|||||||
Execute a HCI command on the SYS channel. The execution is synchronous.
|
Execute a HCI command on the SYS channel. The execution is synchronous.
|
||||||
|
|
||||||
Returns a bytes object with the result of the SYS command.
|
Returns a bytes object with the result of the SYS command.
|
||||||
|
|
||||||
|
Functions specific to STM32WLxx MCUs
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
These functions are available on STM32WLxx microcontrollers, and interact with
|
||||||
|
the integrated "SUBGHZ" radio modem peripheral.
|
||||||
|
|
||||||
|
.. function:: subghz_cs(level)
|
||||||
|
|
||||||
|
Sets the internal SPI CS pin attached to the radio peripheral. The ``level``
|
||||||
|
argument is active-low: a truthy value means "CS pin high" and de-asserts the
|
||||||
|
signal, a falsey value means "CS pin low" and asserts the signal.
|
||||||
|
|
||||||
|
The internal-only SPI bus corresponding to this CS signal can be instantiated
|
||||||
|
using :ref:`machine.SPI()<machine.SPI>` ``id`` value ``"SUBGHZ"``.
|
||||||
|
|
||||||
|
.. function:: subghz_irq(handler)
|
||||||
|
|
||||||
|
Sets the internal SUBGHZ radio interrupt handler to the provided
|
||||||
|
function. The handler function is called as a "hard" interrupt in response to
|
||||||
|
radio peripheral interrupts. See :ref:`isr_rules` for more information about
|
||||||
|
interrupt handlers in MicroPython.
|
||||||
|
|
||||||
|
Calling this function with the handler argument set to None disables the IRQ.
|
||||||
|
|
||||||
|
Due to a hardware limitation, each time this IRQ fires MicroPython disables
|
||||||
|
it before calling the handler. In order to receive another interrupt, Python
|
||||||
|
code should call ``subghz_irq()`` to set the handler again. This has the side
|
||||||
|
effect of re-enabling the IRQ.
|
||||||
|
|
||||||
|
.. function:: subghz_is_busy()
|
||||||
|
|
||||||
|
Return a ``bool`` corresponding to the internal "RFBUSYS" signal from the
|
||||||
|
radio peripheral. Before sending a new command to the radio over SPI then
|
||||||
|
this function should be polled until it returns ``False``, to confirm the
|
||||||
|
busy signal is de-asserted.
|
||||||
|
@ -355,6 +355,7 @@ SRC_C += \
|
|||||||
dac.c \
|
dac.c \
|
||||||
adc.c \
|
adc.c \
|
||||||
sdio.c \
|
sdio.c \
|
||||||
|
subghz.c \
|
||||||
$(wildcard $(BOARD_DIR)/*.c)
|
$(wildcard $(BOARD_DIR)/*.c)
|
||||||
|
|
||||||
SRC_O += \
|
SRC_O += \
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
#define MICROPY_PY_SOCKET (0)
|
#define MICROPY_PY_SOCKET (0)
|
||||||
#define MICROPY_PY_NETWORK (0)
|
#define MICROPY_PY_NETWORK (0)
|
||||||
#define MICROPY_PY_ONEWIRE (0)
|
#define MICROPY_PY_ONEWIRE (0)
|
||||||
#define MICROPY_PY_STM (0)
|
#define MICROPY_PY_STM (1)
|
||||||
#define MICROPY_PY_PYB_LEGACY (0)
|
#define MICROPY_PY_PYB_LEGACY (0)
|
||||||
#define MICROPY_PY_HEAPQ (0)
|
#define MICROPY_PY_HEAPQ (0)
|
||||||
|
|
||||||
|
@ -34,9 +34,9 @@
|
|||||||
,PC0
|
,PC0
|
||||||
,PC1
|
,PC1
|
||||||
,PC2
|
,PC2
|
||||||
,PC3
|
FE_CTRL3,PC3
|
||||||
,PC4
|
FE_CTRL1,PC4
|
||||||
,PC5
|
FE_CTRL2,PC5
|
||||||
,PC6
|
,PC6
|
||||||
SW,PA0
|
SW,PA0
|
||||||
SW1,PA0
|
SW1,PA0
|
||||||
|
|
@ -155,6 +155,8 @@ static inline void restore_irq_pri(uint32_t state) {
|
|||||||
|
|
||||||
#define IRQ_PRI_CAN NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 7, 0)
|
#define IRQ_PRI_CAN NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 7, 0)
|
||||||
|
|
||||||
|
#define IRQ_PRI_SUBGHZ_RADIO NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 8, 0)
|
||||||
|
|
||||||
#define IRQ_PRI_SPI NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 8, 0)
|
#define IRQ_PRI_SPI NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 8, 0)
|
||||||
|
|
||||||
// Interrupt priority for non-special timers.
|
// Interrupt priority for non-special timers.
|
||||||
|
@ -86,6 +86,7 @@
|
|||||||
#include "servo.h"
|
#include "servo.h"
|
||||||
#include "dac.h"
|
#include "dac.h"
|
||||||
#include "can.h"
|
#include "can.h"
|
||||||
|
#include "subghz.h"
|
||||||
|
|
||||||
#if MICROPY_PY_THREAD
|
#if MICROPY_PY_THREAD
|
||||||
STATIC pyb_thread_t pyb_thread_main;
|
STATIC pyb_thread_t pyb_thread_main;
|
||||||
@ -403,6 +404,9 @@ void stm32_main(uint32_t reset_mode) {
|
|||||||
#if defined(STM32WB)
|
#if defined(STM32WB)
|
||||||
rfcore_init();
|
rfcore_init();
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(STM32WL)
|
||||||
|
subghz_init();
|
||||||
|
#endif
|
||||||
#if MICROPY_HW_SDRAM_SIZE
|
#if MICROPY_HW_SDRAM_SIZE
|
||||||
sdram_init();
|
sdram_init();
|
||||||
bool sdram_valid = true;
|
bool sdram_valid = true;
|
||||||
@ -650,6 +654,9 @@ soft_reset_exit:
|
|||||||
#if MICROPY_PY_BLUETOOTH
|
#if MICROPY_PY_BLUETOOTH
|
||||||
mp_bluetooth_deinit();
|
mp_bluetooth_deinit();
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(STM32WL)
|
||||||
|
subghz_deinit();
|
||||||
|
#endif
|
||||||
#if MICROPY_PY_NETWORK
|
#if MICROPY_PY_NETWORK
|
||||||
mod_network_deinit();
|
mod_network_deinit();
|
||||||
#endif
|
#endif
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include "extmod/machine_mem.h"
|
#include "extmod/machine_mem.h"
|
||||||
#include "rfcore.h"
|
#include "rfcore.h"
|
||||||
#include "portmodules.h"
|
#include "portmodules.h"
|
||||||
|
#include "subghz.h"
|
||||||
|
|
||||||
#if MICROPY_PY_STM
|
#if MICROPY_PY_STM
|
||||||
|
|
||||||
@ -51,6 +52,12 @@ STATIC const mp_rom_map_elem_t stm_module_globals_table[] = {
|
|||||||
{ MP_ROM_QSTR(MP_QSTR_rfcore_fw_version), MP_ROM_PTR(&rfcore_fw_version_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_rfcore_fw_version), MP_ROM_PTR(&rfcore_fw_version_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_rfcore_sys_hci), MP_ROM_PTR(&rfcore_sys_hci_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_rfcore_sys_hci), MP_ROM_PTR(&rfcore_sys_hci_obj) },
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(STM32WL)
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_subghz_cs), MP_ROM_PTR(&subghz_cs_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_subghz_irq), MP_ROM_PTR(&subghz_irq_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_subghz_is_busy), MP_ROM_PTR(&subghz_is_busy_obj) },
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC MP_DEFINE_CONST_DICT(stm_module_globals, stm_module_globals_table);
|
STATIC MP_DEFINE_CONST_DICT(stm_module_globals, stm_module_globals_table);
|
||||||
|
139
ports/stm32/subghz.c
Normal file
139
ports/stm32/subghz.c
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Angus Gratton
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "py/gc.h"
|
||||||
|
#include "py/runtime.h"
|
||||||
|
#include "subghz.h"
|
||||||
|
#include "irq.h"
|
||||||
|
#include "spi.h"
|
||||||
|
|
||||||
|
#if defined(STM32WL)
|
||||||
|
|
||||||
|
// Interface to the STM32WL series "SUBGHZ Radio" module
|
||||||
|
|
||||||
|
STATIC void handle_radio_irq() {
|
||||||
|
// Level-triggered interrupts means the interrupt has to be cleared before
|
||||||
|
// this function returns.
|
||||||
|
//
|
||||||
|
// Rather than writing to SUBGHZ SPI in Interrupt Context to clear the
|
||||||
|
// interrupt, disable the IRQ and rely on Python code to call
|
||||||
|
// subghz_irq(handler) to re-enable when needed.
|
||||||
|
HAL_NVIC_DisableIRQ(SUBGHZ_Radio_IRQn);
|
||||||
|
|
||||||
|
mp_obj_t callback = MP_STATE_PORT(subghz_callback);
|
||||||
|
if (callback != mp_const_none) {
|
||||||
|
mp_sched_lock();
|
||||||
|
gc_lock();
|
||||||
|
// Passing dummy 'pin' argument of None, to keep
|
||||||
|
// compatibility with machine.Pin.isr() handlers
|
||||||
|
mp_call_function_1_protected(callback, mp_const_none);
|
||||||
|
gc_unlock();
|
||||||
|
mp_sched_unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SUBGHZ_Radio_IRQHandler(void) {
|
||||||
|
IRQ_ENTER(SUBGHZ_Radio_IRQn);
|
||||||
|
handle_radio_irq();
|
||||||
|
IRQ_EXIT(SUBGHZ_Radio_IRQn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_init(void) {
|
||||||
|
__HAL_RCC_SUBGHZ_RADIO_FORCE_RESET();
|
||||||
|
|
||||||
|
#if !MICROPY_HW_CLK_USE_HSE && MICROPY_HW_CLK_USE_BYPASS
|
||||||
|
// SUBGHZ clock source is HSE oscillator.
|
||||||
|
//
|
||||||
|
// If this is not already enabled for the system clock, and we're depending
|
||||||
|
// on the VDDTCXO pin to power the HSE ("bypass mode"), then enable it.
|
||||||
|
__HAL_RCC_HSE_CONFIG(RCC_HSE_BYPASS_PWR);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NVIC_DisableIRQ(SUBGHZ_Radio_IRQn);
|
||||||
|
NVIC_SetPriority(SUBGHZ_Radio_IRQn, IRQ_PRI_SUBGHZ_RADIO);
|
||||||
|
|
||||||
|
__HAL_RCC_SUBGHZ_RADIO_RELEASE_RESET();
|
||||||
|
|
||||||
|
while (__HAL_RCC_GET_FLAG(RCC_FLAG_RFRST) != 0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
MP_STATE_PORT(subghz_callback) = mp_const_none;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subghz_deinit(void) {
|
||||||
|
MP_STATE_PORT(subghz_callback) = mp_const_none;
|
||||||
|
NVIC_DisableIRQ(SUBGHZ_Radio_IRQn);
|
||||||
|
__HAL_RCC_SUBGHZ_RADIO_FORCE_RESET();
|
||||||
|
__HAL_RCC_SUBGHZ_RADIO_RELEASE_RESET();
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC mp_obj_t subghz_cs(mp_obj_t value) {
|
||||||
|
// Treat the same as normal SPI - truthy is "unselected",
|
||||||
|
// falsey is active low "selected",
|
||||||
|
if (mp_obj_is_true(value)) {
|
||||||
|
LL_PWR_UnselectSUBGHZSPI_NSS();
|
||||||
|
} else {
|
||||||
|
LL_PWR_SelectSUBGHZSPI_NSS();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(subghz_cs_obj, subghz_cs);
|
||||||
|
|
||||||
|
STATIC mp_obj_t subghz_irq(mp_obj_t handler) {
|
||||||
|
MP_STATE_PORT(subghz_callback) = handler;
|
||||||
|
|
||||||
|
if (mp_obj_is_true(handler)) {
|
||||||
|
HAL_NVIC_ClearPendingIRQ(SUBGHZ_Radio_IRQn);
|
||||||
|
HAL_NVIC_EnableIRQ(SUBGHZ_Radio_IRQn);
|
||||||
|
} else {
|
||||||
|
HAL_NVIC_DisableIRQ(SUBGHZ_Radio_IRQn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(subghz_irq_obj, subghz_irq);
|
||||||
|
|
||||||
|
STATIC mp_obj_t subghz_is_busy(void) {
|
||||||
|
// Read the raw unmasked busy signal. This should be checked before driving
|
||||||
|
// CS low to start a command.
|
||||||
|
//
|
||||||
|
// Reads the raw RFBUSYS not the masked RFBUSYMS, in contradiction to EM0453
|
||||||
|
// 6.3 "Radio busy management". This is because the RFBUSYMS signal doesn't
|
||||||
|
// seem to match the reference manual. Observed behaviour matches this bug
|
||||||
|
// report instead: https://community.st.com/s/question/0D53W000014zFx9SAE
|
||||||
|
//
|
||||||
|
// Reading RFBUSYS won't cause any problems here provided a new SPI command
|
||||||
|
// isn't immediately after the previous command, which shouldn't be possible
|
||||||
|
// with MicroPython.
|
||||||
|
return mp_obj_new_bool(LL_PWR_IsActiveFlag_RFBUSYS());
|
||||||
|
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_0(subghz_is_busy_obj, subghz_is_busy);
|
||||||
|
|
||||||
|
MP_REGISTER_ROOT_POINTER(mp_obj_t subghz_callback);
|
||||||
|
|
||||||
|
#endif // STM32WL
|
40
ports/stm32/subghz.h
Normal file
40
ports/stm32/subghz.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Angus Gratton
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef MICROPY_INCLUDED_STM32_SUBGHZ_H
|
||||||
|
#define MICROPY_INCLUDED_STM32_SUBGHZ_H
|
||||||
|
|
||||||
|
#include "py/obj.h"
|
||||||
|
|
||||||
|
// Interface to the STM32WL series "SUBGHZ Radio" module
|
||||||
|
|
||||||
|
void subghz_init(void);
|
||||||
|
void subghz_deinit(void);
|
||||||
|
|
||||||
|
MP_DECLARE_CONST_FUN_OBJ_1(subghz_cs_obj);
|
||||||
|
MP_DECLARE_CONST_FUN_OBJ_1(subghz_irq_obj);
|
||||||
|
MP_DECLARE_CONST_FUN_OBJ_0(subghz_is_busy_obj);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user