1301 views|2 replies

108

Posts

8

Resources
The OP
 

[Automatic clock-in walking timing system based on face recognition] Node communication - LoRa technology [Copy link]

 

The LoRa debugging process mentioned earlier has been delayed due to business trips, and I haven’t had time to write a post to share it. Today it’s finally here~~~~

Debugging tasks

  • Use nRF52832 (Bluetooth SoC) as the main control, drive Semtech's SX1262 (compatible with LLCC68 chip) through SPI, and realize point-to-point LoRa communication between two boards
  • Why use 52832? It is because there are other functions on the Bluetooth chip.Allow me to keep you in suspense for now.~~
  • Finally, use nRF52832 to connect and communicate via UART and the UART of the MAIXBIT K210 board

Chip Specifications

  • First, check the specification of LLCC68 from Semtech.

  • There is a lot of information about nRF52832 development. You can pay attention to online information (for example, https://www.cnblogs.com/iini/ This blog has many introductory articles)
  • Go to the official link to download the SDK: https://developer.nordicsemi.com/
  • The SDK I used when debugging is based on the SDK code package of version V17.1.0

Pin connection

  • Identify the circuit of SX1262/LLCC68 and find out the SPI and DIO pins connected to the main controller.
  • The nRF52832 main control uses a module, and its pin connection relationship can be found

  • The pin connections are mainly SPI, RESET and DIO


Driver interface code

Code part,

  • You need to download the SX126X driver code from Semtech's GitHub repository: https://github.com/Lora-net/sx126x_driver
  • Or choose to download the driver code of LLCC68: https://github.com/Lora-net/llcc68_driver
  • Next, you need to debug the SPI driver. Since there are many ready-made APIs in the nrfx SPI driver in the nRF52832 SDK, you don’t need to pay attention to the registers and can just call the API.
#include "board.h"
#include "spi-board.h"


#define SPI_INSTANCE           0 /**< SPI instance index. */
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);  /**< SPI instance. */

void SpiInit( Spi_t *obj, PinNames mosi, PinNames miso, PinNames sclk, PinNames nss )
{
   //SPI Gpio_t
   obj->Mosi.mode = PIN_OUTPUT;
   obj->Mosi.pull = PIN_PULL_UP;
   obj->Mosi.pin  = mosi;
   
   obj->Miso.mode = PIN_INPUT;
   obj->Miso.pull = PIN_PULL_UP;
   obj->Miso.pin  = miso;
  
   obj->Sclk.mode = PIN_OUTPUT;
   obj->Sclk.pull = PIN_PULL_UP;
   obj->Sclk.pin  = sclk;
  
   obj->Nss.mode = PIN_OUTPUT;
   obj->Nss.pull = PIN_PULL_UP;
   obj->Nss.pin = nss;
  
   //spi interface config
   obj->config.irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY;
   obj->config.orc          = 0xFF;
   obj->config.frequency    = NRF_DRV_SPI_FREQ_1M;
   obj->config.mode         = NRF_DRV_SPI_MODE_0;
   obj->config.bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST;
  
   obj->config.mosi_pin = mosi;
   obj->config.miso_pin = miso;
   obj->config.sck_pin  = sclk;
#if (HW_SPI == 1)  
   obj->config.ss_pin   = nss;
#else
   obj->config.ss_pin   = NC;
#endif
  
#if (SPI_INSTANCE == 0)  
   obj->spi = NRF_SPI0;
#elif (SPI_INSTANCE == 1)
   obj->spi = NRF_SPI1;
#elif (SPI_INSTANCE == 2)
   obj->spi = NRF_SPI2;
#else
  #error "no spi interface"
#endif
   
   APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &obj->config, NULL, NULL));
	 
   //sw gpio => nss config
   GpioInit( &obj->Nss, obj->Nss.pin, obj->Nss.mode, PIN_PUSH_PULL, obj->Nss.pull, 1 );

	 nrf_spi_int_disable(obj->spi, NRF_SPI_INT_READY_MASK);
   
	 nrf_spi_enable(obj->spi);
}

void SpiDeInit( Spi_t *obj )
{
    nrf_drv_spi_uninit(&spi);
  
    GpioInit( &obj->Mosi, obj->Mosi.pin, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    GpioInit( &obj->Miso, obj->Miso.pin, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    GpioInit( &obj->Sclk, obj->Sclk.pin, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
    GpioInit( &obj->Nss,  obj->Nss.pin,  PIN_ANALOGIC,  PIN_PUSH_PULL, PIN_PULL_UP, 1 );
}


uint16_t SpiInOut( Spi_t *obj, uint16_t outData )
{
    uint8_t rxData = 0;

    nrf_spi_event_clear(obj->spi, NRF_SPI_EVENT_READY);

    nrf_spi_txd_set(obj->spi, outData);

	  while (!nrf_spi_event_check(obj->spi, NRF_SPI_EVENT_READY)) {}
			
		nrf_spi_event_clear(obj->spi, NRF_SPI_EVENT_READY);
       
    rxData = nrf_spi_rxd_get(obj->spi);
      
    return( rxData );
}
  • GPIO interrupt driver part, realizes IO control and IO interrupt configuration
#include "board.h"
#include "gpio-board.h"

static GpioIrqHandler *GpioIrq[31];

void GpioMcuInit( Gpio_t *obj, PinNames pin, PinModes mode, PinConfigs config, PinTypes type, uint32_t value )
{

    obj->pin = pin;

    if( pin == NC )
    {
        return;
    }

    obj->mode = mode;
    obj->pull = type;

    if( mode == PIN_INPUT ) //gpio input
    {
        if( config == PIN_PUSH_PULL )
        {
            if (type == PIN_PULL_UP)
            {
              nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_PULLUP);
            } else if (type == PIN_PULL_DOWN)
            {
              nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_PULLDOWN);
            } else {
              nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_NOPULL);
            }
        }
        else
        {
            nrf_gpio_cfg_default(pin);
        }
    }
    else if( mode == PIN_ANALOGIC ) //adc
    {
        nrf_gpio_cfg_default(pin);
    }
    else if( mode == PIN_OUTPUT ) //gpio output
    {
        nrf_gpio_cfg_output(pin);
      
        GpioMcuWrite( obj, value );
    }
    
    return;
}

void HAL_Gpio_Interrupt_Handle(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
  if (pin > 31)
  {
    return;
  }
  
  if (GpioIrq[pin] != NULL)
  {
    GpioIrq[pin]();
  }
}

void GpioMcuSetInterrupt( Gpio_t *obj, IrqModes irqMode, IrqPriorities irqPriority, GpioIrqHandler *irqHandler )
{

    uint32_t priority = 0;

    if( irqHandler == NULL )
    {
        return;
    }
 
    ret_code_t err_code;
		
		if (!nrf_drv_gpiote_is_init())
    {
				err_code = nrf_drv_gpiote_init();
				APP_ERROR_CHECK(err_code);
		}
    
    if( irqMode == IRQ_RISING_EDGE )
    {
      nrf_drv_gpiote_in_config_t rising_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(false);
      if (obj->pull == PIN_PULL_UP)
      {
        rising_config.pull = NRF_GPIO_PIN_PULLUP;
      } else if (obj->pull == PIN_PULL_DOWN) {
        rising_config.pull = NRF_GPIO_PIN_PULLDOWN;
      } else {
        rising_config.pull = NRF_GPIO_PIN_NOPULL;
      }
      
      err_code = nrf_drv_gpiote_in_init(obj->pin, &rising_config, HAL_Gpio_Interrupt_Handle);
      APP_ERROR_CHECK(err_code);
    }
    else if( irqMode == IRQ_FALLING_EDGE )
    {
      nrf_drv_gpiote_in_config_t falling_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(false);
      if (obj->pull == PIN_PULL_UP)
      {
        falling_config.pull = NRF_GPIO_PIN_PULLUP;
      } else if (obj->pull == PIN_PULL_DOWN) {
        falling_config.pull = NRF_GPIO_PIN_PULLDOWN;
      } else {
        falling_config.pull = NRF_GPIO_PIN_NOPULL;
      }
      
      err_code = nrf_drv_gpiote_in_init(obj->pin, &falling_config, HAL_Gpio_Interrupt_Handle);
      APP_ERROR_CHECK(err_code);
    }
    else
    {
      nrf_drv_gpiote_in_config_t toggle_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(false);
      if (obj->pull == PIN_PULL_UP)
      {
        toggle_config.pull = NRF_GPIO_PIN_PULLUP;
      } else if (obj->pull == PIN_PULL_DOWN) {
        toggle_config.pull = NRF_GPIO_PIN_PULLDOWN;
      } else {
        toggle_config.pull = NRF_GPIO_PIN_NOPULL;
      }
      
      err_code = nrf_drv_gpiote_in_init(obj->pin, &toggle_config, HAL_Gpio_Interrupt_Handle);
      APP_ERROR_CHECK(err_code);
    }

    switch( irqPriority )
    {
    case IRQ_VERY_LOW_PRIORITY:
    case IRQ_LOW_PRIORITY:
        priority = 3;
        break;
    case IRQ_MEDIUM_PRIORITY:
        priority = 2;
        break;
    case IRQ_HIGH_PRIORITY:
        priority = 1;
        break;
    case IRQ_VERY_HIGH_PRIORITY:
    default:
        priority = 0;
        break;
    }

    GpioIrq[obj->pin] = irqHandler;

    nrf_drv_gpiote_in_event_enable(obj->pin, true);
}

void GpioMcuRemoveInterrupt( Gpio_t *obj )
{
   nrf_drv_gpiote_in_event_enable(obj->pin, false);
}

void GpioMcuWrite( Gpio_t *obj, uint32_t value )
{
    if( obj == NULL )
    {
        assert_param( FAIL );
    }
    
    // Check if pin is not connected
    if( obj->pin == NC )
    {
        return;
    }
    
    nrf_gpio_pin_write(obj->pin, value);
}

void GpioMcuToggle( Gpio_t *obj )
{
    if( obj == NULL )
    {
        assert_param( FAIL );
    }

    // Check if pin is not connected
    if( obj->pin == NC )
    {
        return;
    }

    nrf_gpio_pin_toggle(obj->pin);
}

uint32_t GpioMcuRead( Gpio_t *obj )
{
		uint8_t pinstate = 0;
	
    if( obj == NULL )
    {
        assert_param( FAIL );
    }
    
    // Check if pin is not connected
    if( obj->pin == NC )
    {
        return 0;
    }
    
#if 1		
		if(nrf_gpio_pin_dir_get(obj->pin) == NRF_GPIO_PIN_DIR_INPUT)
		{
				pinstate = nrf_gpio_pin_read(obj->pin);
		}
		else
		{
				pinstate = nrf_gpio_pin_out_read(obj->pin);		
		}
		return pinstate;
		
#else			
    return nrf_gpio_pin_read(obj->pin);
#endif	
}
  • In addition, since the driver written by Semtech uses a timer, it is called for the timing of sending and receiving, which leads to the need to implement the corresponding timer driver
#include <math.h>
#include "board.h"
#include "timer-board.h"


const nrf_drv_rtc_t rtc = NRF_DRV_RTC_INSTANCE(2); /**< Declaring an instance of nrf_drv_rtc for RTC2. */

void TIMER_IRQHandler(nrf_drv_rtc_int_type_t int_type);

/*!
 * Hardware Time base in ms
 */
#define HW_TIMER_TIME_BASE 1 //ms

/*!
 * Hardware Timer tick counter
 */
volatile TimerTime_t TimerTickCounter = 1;

/*!
 * Saved value of the Tick counter at the start of the next event
 */
static TimerTime_t TimerTickCounterContext = 0;

/*!
 * Value trigging the IRQ
 */
volatile TimerTime_t TimeoutCntValue = 0;

/*!
 * Increment the Hardware Timer tick counter
 */
void TimerIncrementTickCounter(void);

/*!
 * Counter used for the Delay operations
 */
volatile uint32_t TimerDelayCounter = 0;

/*!
 * Retunr the value of the counter used for a Delay
 */
uint32_t TimerHwGetDelayValue(void);

/*!
 * Increment the value of TimerDelayCounter
 */
void TimerIncrementDelayCounter(void);

void TimerHwInit(void)
{
	  /* TIMER clock enable */
    //LFCLK Initial
  
	  /* TIMER interrupt Init */
    uint32_t err_code;

    //Initialize RTC instance
    nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG;
#if 1  
    config.prescaler = 31;
#else
    config.prescaler = 0;
#endif
  
    err_code = nrf_drv_rtc_init(&rtc, &config, TIMER_IRQHandler);
    APP_ERROR_CHECK(err_code);
  
#if 1
    //Enable tick event & interrupt
    nrf_drv_rtc_tick_enable(&rtc, true);
#else

    //Set compare channel to trigger interrupt after COMPARE_COUNTERTIME seconds
    err_code = nrf_drv_rtc_cc_set(&rtc, 0,  RTC_DEFAULT_CONFIG_FREQUENCY/1000, true);
    APP_ERROR_CHECK(err_code);
#endif    
}

void TimerHwDeInit(void)
{
    /* TIMER clock enable */
    //Power uninit RTC instance
    nrf_drv_rtc_uninit(&rtc);
}

uint32_t TimerHwGetMinimumTimeout(void)
{
    return (ceil(2 * HW_TIMER_TIME_BASE));
}

void TimerHwStart(uint32_t val)
{
    TimerTickCounterContext = TimerHwGetTimerValue();

    if (val <= HW_TIMER_TIME_BASE + 1)
    {
        TimeoutCntValue = TimerTickCounterContext + 1;
    }
    else
    {
        TimeoutCntValue = TimerTickCounterContext + ((val - 1) / HW_TIMER_TIME_BASE);
    }
    
    //Power on RTC instance
    nrf_drv_rtc_enable(&rtc);
}

void TimerHwStop(void)
{
   //Power on RTC instance
    nrf_drv_rtc_disable(&rtc);
}

TimerTime_t TimerHwGetElapsedTime(void)
{
    return (((TimerHwGetTimerValue() - TimerTickCounterContext) + 1) * HW_TIMER_TIME_BASE);
}

TimerTime_t TimerHwGetTimerValue(void)
{
    TimerTime_t val = 0;

    __disable_irq();

    val = TimerTickCounter;

    __enable_irq();

    return (val);
}

TimerTime_t TimerHwComputeElapsedTime(TimerTime_t eventInTime)
{
    TimerTime_t elapsedTime = 0;

    // Needed at boot, cannot compute with 0 or elapsed time will be equal to current time
    if( eventInTime == 0 )
    {
        return 0;
    }

    elapsedTime = TimerHwGetTimerValue( );

    if( elapsedTime < eventInTime )
    { // roll over of the counter
        return( elapsedTime + ( 0xFFFFFFFF - eventInTime ) );
    }
    else
    {
        return( elapsedTime - eventInTime );
    }    
}

TimerTime_t TimerHwGetTime(void)
{

    return TimerHwGetTimerValue() * HW_TIMER_TIME_BASE;
}

uint32_t TimerHwGetDelayValue(void)
{
    uint32_t val = 0;

    __disable_irq();

    val = TimerDelayCounter;

    __enable_irq();

    return (val);
}

void TimerIncrementTickCounter(void)
{
    __disable_irq();

    TimerTickCounter++;

    __enable_irq();
}

void TimerIncrementDelayCounter(void)
{
    __disable_irq();

    TimerDelayCounter++;

    __enable_irq();
}

/*!
 * Timer IRQ handler
 */

void TIMER_IRQHandler(nrf_drv_rtc_int_type_t int_type)
{  
    if (int_type == NRF_DRV_RTC_INT_TICK)
    {
      TimerIncrementTickCounter();

      if (TimerTickCounter == TimeoutCntValue)
      {
          TimerIrqHandler();
      }
    }
    else if (int_type == NRF_DRV_RTC_INT_COMPARE0)
    {

      TimerIncrementTickCounter();

      if (TimerTickCounter == TimeoutCntValue)
      {
          TimerIrqHandler();
      }
    }
}

Main task part

#include "task_lora.h"
#include "board.h"

void lora_rx_handler(uint8_t *payload)
{
		switch(payload[4])
		{
				case 'r':
						BoardLed_Flashing(RGBLED_RED);
						Radio.Send("red flashing", sizeof("red flashing"));
						//Radio.Send("led_red", sizeof("led_red"));
				break;

				case 'g':
						BoardLed_Flashing(RGBLED_GREEN);
						Radio.Send("green flashing", sizeof("green flashing"));
				break;
				
				case 'b':
						BoardLed_Flashing(RGBLED_BLUE);
						Radio.Send("blue flashing", sizeof("blue flashing"));
				break;
				
				default:
					break;
		}
}


void OnTxDone( void )
{
		printf("function: %s", __FUNCTION__);
		NRF_LOG_DEBUG("function: %s", __FUNCTION__);
		Radio.Standby();
		Radio.Rx(0xFFFFFF);
}

void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
{
	  printf("function: %s", __FUNCTION__);
		NRF_LOG_DEBUG("function: %s", __FUNCTION__);
		printf("receive:%s, rssi:%d, snr%d\r\n", payload, rssi, snr);
		lora_rx_handler(payload);
//		Radio.Standby();
//		Radio.Rx(0xFFFFFF);
}

void OnTxTimeout( void )
{
	  printf("function: %s", __FUNCTION__);
		NRF_LOG_INFO("function: %s", __FUNCTION__);
		Radio.Standby();
		Radio.Rx(0xFFFFFF);
}

void OnRxTimeout( void )
{
	  printf("function: %s", __FUNCTION__);
		NRF_LOG_INFO("function: %s", __FUNCTION__);
		Radio.Standby();
		Radio.Rx(0xFFFFFF);
}

void OnRxError( void )
{
	  printf("function: %s", __FUNCTION__);
		NRF_LOG_INFO("function: %s", __FUNCTION__);
		Radio.Standby();
		Radio.Rx(0xFFFFFF);
}


static RadioEvents_t RadioEvents;

void lora_init(void)
{
		RadioEvents.TxDone = OnTxDone;
    RadioEvents.RxDone = OnRxDone;
    RadioEvents.TxTimeout = OnTxTimeout;
    RadioEvents.RxTimeout = OnRxTimeout;
    RadioEvents.RxError = OnRxError;
	
		Radio.Init( &RadioEvents );
		
		Radio.SetMaxPayloadLength(MODEM_LORA, MAX_PAYLOADLENGTH);
	
    Radio.SetTxConfig( MODEM_LORA, LORA_TX_POWER, 0, LORA_BW, LORA_SF, LORA_CR, LORA_PREAMBLE, 
											LORA_FIX_LENGTH_PAYLOAD_ON, true, 0, 0, LORA_IQ_INVERSION_ON, TX_TIMEOUT_VALUE );
	
    Radio.SetRxConfig( MODEM_LORA, LORA_BW, LORA_SF,
                       LORA_CR, 0, LORA_PREAMBLE,
                       LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
                       0, true, 0, 0, LORA_IQ_INVERSION_ON, true );

    Radio.SetChannel( LORA_RX_FREQ );	

		Radio.Rx(0xFFFFFF);
}

void lora_process(void)
{
		//if receive from uart
		
		//if DIO1 irq
		Radio.IrqProcess();
}


/**[url=home.php?mod=space&uid=159083]@brief[/url] Application main function.
 */
int main(void)
{
    // Initialize.
    uart_init();
    log_init();
    timers_init();
    power_management_init();
    ble_stack_init();
    gap_params_init();
    gatt_init();
    services_init();
    advertising_init();
    conn_params_init();

    // Start execution.
    printf("\r\nBLE-LoRa Init...\r\n");
		NRF_LOG_INFO("Debug logging for UART over RTT started.");

#if BLE_ENABLE
		advertising_start();
#endif
	
		board_lora_init();
	
    // Enter main loop.
    for (;;)
    {
				lora_process();
					
        idle_state_handle();
    }
}

This post is from DigiKey Technology Zone

Latest reply

Next, you need to debug the SPI driver, because the nRF52832 SDK contains the nrfx SPI driver. Is this chip used a lot now?   Details Published on 2022-9-17 22:58
 
 

6818

Posts

11

Resources
2
 

Next, you need to debug the SPI driver, because the nRF52832 SDK contains the nrfx SPI driver.

Is this chip used a lot now?

This post is from DigiKey Technology Zone

Comments

It is quite common in BLE applications. . . . However, the chip price increase/shortage has also had a big impact. . . .  Details Published on 2022-9-20 15:32
 
 
 

108

Posts

8

Resources
3
 
lugl4313820 posted on 2022-9-17 22:58 Then you need to debug the SPI driver, because the nRF52832 SDK contains the nrfx SPI driver. Is this chip used a lot now? ...

It is quite common in BLE applications. . . . However, the chip price increase/shortage has also had a big impact. . . .

This post is from DigiKey Technology Zone
 
 
 

Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list