STM32 HAL Driver para módulos RFM95 Lorawan.
Para projetos baseados em CMake (por exemplo, ao usar os projetos STM32CUBEMX com Clion), você pode, por exemplo, configurar o driver como submodule e link para ele:
add_subdirectory ( "<directory-to-driver>/rfm95" )
target_link_libraries (< target - name > stm32-hal-rfm95)Depois de conectar o módulo RFM95 ao seu microcontrolador via SPI e inicializar o barramento usando o cubo, você pode usar esta biblioteca para interagir com o módulo RFM95, conforme mostrado no exemplo a seguir. Você deve lidar com DIO0, 1 e 5 interrupções:
int main () {
// Create the handle for the RFM95 module.
rfm95_handle_t rfm95_handle = {
. spi_handle = & hspi1 ,
. nss_port = RFM95_NSS_GPIO_Port ,
. nss_pin = RFM95_NSS_Pin ,
. nrst_port = RFM95_NRST_GPIO_Port ,
. nrst_pin = RFM95_NRST_Pin ,
. device_address = {
0x00 , 0x00 , 0x00 , 0x00
},
. application_session_key = {
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
},
. network_session_key = {
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
},
. receive_mode = RFM95_RECEIVE_MODE_NONE
};
// Initialise RFM95 module.
if (! rfm95_init ( & rfm95_handle )) {
printf ( "RFM95 init failednr" );
}
uint8_t [] data_packet = {
0x01 , 0x02 , 0x03 , 0x4
};
if (! rfm95_send_receive_cycle ( & rfm95_handle , data_packet , sizeof ( data_packet ))) {
printf ( "RFM95 send failednr" );
} else {
printf ( "RFM95 send successnr" );
}
}
void HAL_GPIO_EXTI_Callback ( uint16_t GPIO_Pin )
{
if ( GPIO_Pin == RFM95_DIO0_Pin ) {
rfm95_on_interrupt ( & rfm95_handle , RFM95_INTERRUPT_DIO0 );
} else if ( GPIO_Pin == RFM95_DIO1_Pin ) {
rfm95_on_interrupt ( & rfm95_handle , RFM95_INTERRUPT_DIO1 );
} else if ( GPIO_Pin == RFM95_DIO5_Pin ) {
rfm95_on_interrupt ( & rfm95_handle , RFM95_INTERRUPT_DIO5 );
}
} As funções reload_config e save_config podem ser usadas para armazenar e recuperar contadores de quadros RX e TX, bem como outras configurações dentro/a partir da memória não volátil. Por exemplo, ao usar minha biblioteca EEPROM (https://github.com/henriheimann/stm32-hal-eeprom) para armazenar os contadores de quadros, um exemplo de implementação pode parecer o seguinte:
// Forward declaration of the frame counter functions.
static bool reload_config ( rfm95_eeprom_config_t * config );
static void save_config ( const rfm95_eeprom_config_t * config );
// Create the handle for the RFM95 module.
rfm95_handle_t rfm95_handle = {
// ... see example above
. reload_config = reload_config ,
. save_config = save_config ,
};
// Create the EEPROM handle.
eeprom_handle_t eeprom_handle = {
. i2c_handle = & hi2c1 ,
. device_address = EEPROM_24LC32A_ADDRESS ,
. max_address = EEPROM_24LC32A_MAX_ADDRESS ,
. page_size = EEPROM_24LC32A_PAGE_SIZE
};
static bool reload_config ( rfm95_eeprom_config_t * config )
{
return eeprom_read_bytes ( & eeprom_handle , 0x0000 , ( uint8_t * ) config , sizeof ( rfm95_eeprom_config_t ));
}
static void save_config ( const rfm95_eeprom_config_t * config )
{
eeprom_write_bytes ( & eeprom_handle , 0x0000 , ( uint8_t * ) config , sizeof ( rfm95_eeprom_config_t ));
}Se você deseja usar a capacidade da biblioteca de receber mensagens de downlink com comandos MAC que podem configurar o dispositivo final, é necessário fornecer relógio de precisão e atraso na funcionalidade, além de algumas outras funções. Um exemplo de implementação usando o STM32L4 LPTIM1 pode parecer algo assim:
// Forward declaration of the functions.
static uint32_t get_precision_tick ();
static void precision_sleep_until ( uint32_t target_ticks );
static uint8_t random_int ( uint8_t max );
static uint8_t get_battery_level ();
// Create the handle for the RFM95 module.
rfm95_handle_t rfm95_handle = {
// ... see example above
. precision_tick_frequency = 32768 ,
. precision_tick_drift_ns_per_s = 5000 ,
. receive_mode = RFM95_RECEIVE_MODE_RX12 ,
. get_precision_tick = get_precision_tick ,
. precision_sleep_until = precision_sleep_until ,
. random_int = random_int ,
. get_battery_level = get_battery_level
};
volatile uint32_t lptim_tick_msb = 0 ;
static uint32_t get_precision_tick ()
{
__disable_irq ();
uint32_t precision_tick = lptim_tick_msb | HAL_LPTIM_ReadCounter ( & hlptim1 );
__enable_irq ();
return precision_tick ;
}
void HAL_LPTIM_AutoReloadMatchCallback ( LPTIM_HandleTypeDef * hlptim )
{
lptim_tick_msb += 0x10000 ;
}
static void precision_sleep_until ( uint32_t target_ticks )
{
while (true) {
uint32_t start_ticks = get_precision_tick ();
if ( start_ticks > target_ticks ) {
break ;
}
uint32_t ticks_to_sleep = target_ticks - start_ticks ;
// Only use sleep for at least 10 ticks.
if ( ticks_to_sleep >= 10 ) {
// Calculate required value of compare register for the sleep minus a small buffer time to compensate
// for any ticks that occur while we perform this calculation.
uint32_t compare = ( start_ticks & 0xffff ) + ticks_to_sleep - 2 ;
// If the counter auto-reloads we will be woken up anyway.
if ( compare > 0xffff ) {
HAL_SuspendTick ();
HAL_PWREx_EnterSTOP2Mode ( PWR_STOPENTRY_WFI );
HAL_ResumeTick ();
// Otherwise, set compare register and use the compare match interrupt to wake up in time.
} else {
__HAL_LPTIM_COMPARE_SET ( & hlptim1 , compare );
while (! __HAL_LPTIM_GET_FLAG ( & hlptim1 , LPTIM_FLAG_CMPOK ));
__HAL_LPTIM_CLEAR_FLAG ( & hlptim1 , LPTIM_FLAG_CMPM );
__HAL_LPTIM_ENABLE_IT ( & hlptim1 , LPTIM_IT_CMPM );
HAL_SuspendTick ();
HAL_PWREx_EnterSTOP2Mode ( PWR_STOPENTRY_WFI );
HAL_ResumeTick ();
__HAL_LPTIM_DISABLE_IT ( & hlptim1 , LPTIM_IT_CMPM );
}
} else {
break ;
}
// Busy wait until we have reached the target.
while ( get_precision_tick () < target_ticks );
}
static uint8_t random_int ( uint8_t max )
{
return 0 ; // Use ADC other means of obtaining a random number.
}
static uint8_t get_battery_level ()
{
return 0xff ; // 0xff = Unknown battery level.
} Os microcontroladores STM32L0, STM32L4 e STM32F4 são suportados. O cabeçalho HAL inclui para outros microcontroladores pode ser adicionado no rfm95.h .