SX1276 RegOpMode register - cant change mode

Hi,

I am struggling to get the SX1276 radio module in different kind of modes.

No matter what I do, the mode will only be in sleep mode.

I can change between LoRa and FSK/OOK Mode, but when I write 0x81 for LoRa mode and standby in the RegOpMode register, and read back the value I get 0x80, which is LoRa mode and sleep.

Is this normal? Am I doing something wrong?

Thanks

How does your code compare to the reference driver?

I have the following functions:

void spi_write_sx1276(uint8_t address, uint8_t data);

and

uint8_t spi_read_sx1276(uint8_t address);

I do the following to get it in LoRa Mode:

spi_write_sx1276(REG_OPMODE, LORA_MODE_SLEEP);
spi_write_sx1276(REG_OPMODE, LORA_MODE_FLAG_LONG_RANGE);
spi_write_sx1276(REG_OPMODE, LORA_MODE_FLAG_LONG_RANGE | LORA_MODE_STDBY);

Where

REG_OPMODE = 0x01
LORA_MODE_SLEEP = 0x00
LORA_MODE_FLAG_LONG_RANGE = 0x80
LORA_MODE_STDBY = 0x01

I have taken care of bit 7, which indicates read/write operations as of the SPI documentation.

After that, I read back the value, with the SPI read function.

spi_read_sx1276(REG_OPMODE)

But the return value is

0x80

Which indicates it has changed to LoRa mode, but is still in sleep mode, even though I send the LORA_MODE_STDBY parameter. I have tried other modes too, but without luck.

Even after reset, where I read the REG_OPMODE register directly I get

0x08

Which is indicating that bit 0 is not set, as it should be as of the datasheet.

Did you ever resolve this issue? i’m encountering the same issue right now and cannot figure it out

Which library are you using, most of the known TTN compatible ones should handle this without a problem ?

I am having the same issue right now. Has anyone solved it?

I dont see what needs to be ‘solved’, the value of REG_OPMODE is 0x09 directly after the SX127x device reset, at least it is on mine.

Which library are you using with the device ?

But in my case it is directly 0x08 after power up. Then I initialize all the registers and set RegOpMode to 0x81 or 0x89. In all cases, all the registers are successfully set except the last three bits of RegOpMode. What might cause sx1276 to not go to STANDBY? Maybe I am not setting up something before power up. I am writing my own code but I use the libraries from Semtech as reference. Anyway, does it matter? Because right after power up, sx1276 should be in standby mode. Something prevents the chip to switch to standby and I cannot find it.

Well, as I said, all my SX127x follow the datasheet.

Perhaps show us your code and say which library you are using ?

Thank you for your attention. I am using CMWX1ZZABZ board(https://wireless.murata.com/pub/RFM/data/type_abz.pdf) board. It has stm32l072 or stm32l082 with sx1276. For example, I am trying the following code. I see 8 and 128 from UART, respectively.

void LoraInit()
{
 LoraReset();
printUART(LoraReadByte(RegOpMode)); //print to UART, shows 8

LoraSetXO(SET);
//LoraReset();
LoraWriteByte(RegOpMode,0x80);
//LoraChangeMode(SLEEP);
LoraSetChannel(868); //Radio Freq is 868 MHz
LoraWriteByte(RegPaConfig,0x4F);
LoraWriteByte(RegHopPeriod,0x00);


LoraWriteByte(RegModemConfig1,0x73); // 01110011 -> Bw = 125KHz, CodingRate = 4/5, Mode = Implicit
LoraWriteByte(RegModemConfig2,0x70); // 01110000 -> SpreadingFactor = 7, TxMode = Normal, CRC Disabled
LoraWriteByte(RegModemConfig3,0x00);
LoraWriteByte(RegPreambleMsb, 0x00);
LoraWriteByte(RegPreambleLsb, 0x80);
LoraWriteByte(RegSymTimeoutLsb,0x64);
LoraWriteByte(RegPayloadLength,0x1); //Payload Length is always 1 byte because Mode is Implicit
LoraWriteByte(RegDetectOptimize, (LoraReadByte(RegDetectOptimize) & 0xF8) | (0x03));
LoraWriteByte(RegDetectionThreshold, 0x0A);
LoraWriteByte(RegInvertIQ, (LoraReadByte(RegInvertIQ) & ~(0x41)));
LoraWriteByte(RegInvertIQ2, 0x1D);
LoraWriteByte(IfFreq2, 0x00);
LoraWriteByte(IfFreq1, 0x40);

//LoraWriteByte(RegOpMode,0x80);
LoraWriteByte(RegOpMode,0x89);

//HAL_Delay(100);

//LoraChangeMode(STANDBY);
HAL_Delay(5);
//LoraChangeMode(RXSINGLE);
printUART(LoraReadByte(RegOpMode)); //print to UART, shows 136

}

But thats not (all) the code you are using.

Which library are you using (third time I have asked) ?

And by ‘library’ I also mean the source of the fuctions you are calling.

What is the application here, the code looks most unlike anything related to The Things Network ?

Going from Sleep to Standby takes about 160–190µs. According to the data sheet (section 2.5.2, value for TS_OSC / oscillator wake-up time) it’s even 250µs. Also see section 4.2.5 Startup Times.

The OpMode register will not reflect the new state until the new mode has been reached. So just delay for a short moment and read the register again.

It seems the problem is related to Murata CMWX1ZZABZ board. Today I used an STM32F429 with separate SX1276 chip and used the same code. It shows TxDone and RxTimeout on the console. Now I am going to write Tx and Rx to two separate devices to see if they can communicate. But I get 0x09 at the startup as expected. The full code is here:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
//#define SLEEP 0
//#define STANDBY 1
//#define TX 2
//#define RXSINGLE 3
//#define RXCONTINUOUS 4

typedef enum
{
	SLEEP = 0,
	STANDBY,
	TX,
	RXSINGLE,
	RXCONTINUOUS
}Modes;

typedef enum
{
	FSK = 0,
	LORA
}States;
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
uint8_t RegOpMode = 0x01;
uint8_t RegFrMsb = 0x06;
uint8_t RegFrMid = 0x07;
uint8_t RegFrLsb = 0x08;
uint8_t RegPaConfig = 0x09;
uint8_t RegHopPeriod = 0x24;
uint8_t RegModemConfig1 = 0x1D;
uint8_t RegModemConfig2 = 0x1E;
uint8_t RegModemConfig3 = 0x26;
uint8_t RegPreambleMsb = 0x20;
uint8_t RegPreambleLsb = 0x21;
uint8_t RegPayloadLength = 0x22;
uint8_t RegSymTimeoutLsb = 0x1F;
uint8_t RegDetectOptimize = 0x31;
uint8_t RegDetectionThreshold = 0x37;
uint8_t RegInvertIQ = 0x33;
uint8_t RegInvertIQ2 = 0x3B;
uint8_t IfFreq2 = 0x2F;
uint8_t IfFreq1 = 0x30;

uint8_t RegFifo = 0x00;
uint8_t RegFifoAddrPtr = 0x0D;
uint8_t RegFifoTxBaseAddr = 0x0E;
uint8_t RegFifoRxBaseAddr = 0x0F;
uint8_t RegFifoRxCurrentAddr = 0x10; //read_only

uint8_t RegIrqFlags = 0x12;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
void printUART(uint8_t byte);

uint8_t LoraReadByte(uint8_t address);

void LoraWriteByte(uint8_t address, uint8_t data);

void LoraSetXO(uint8_t state);

void LoraReset();

void LoraSwitchModem(uint8_t modem);

void LoraInit();

void LoraSetChannel(uint32_t freq); //freq in Mhz, for example 868

void LoraTx(uint8_t byte);

uint8_t LoraRx();

void printPayload();
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */
  

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI1_Init();
  /* USER CODE BEGIN 2 */
  LoraInit();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  LoraTx(0x55);
	  LoraRx();
	  HAL_Delay(10);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 64;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV2;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */
void printUART(uint8_t byte)
{
//	  uint8_t buf[16];
//	  snprintf((char*)buf,16,"%d\r\n",byte);
	  printf("data= %d\r\n",byte);
	  //HAL_Delay(1);
}

uint8_t LoraReadByte(uint8_t address)
{
	uint8_t rxByte;
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,RESET);

	address = (address & 0x7F);
	HAL_SPI_Transmit(&hspi1,&address,1,HAL_MAX_DELAY);
	HAL_SPI_Receive(&hspi1,&rxByte,1,HAL_MAX_DELAY);

	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,SET);

	return rxByte;
}


void LoraWriteByte(uint8_t address, uint8_t data)
{
	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,RESET);

	address = (address | 0x80 );
	HAL_SPI_Transmit(&hspi1,&address,1,HAL_MAX_DELAY);
	HAL_SPI_Transmit(&hspi1,&data,1,HAL_MAX_DELAY);

	HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,SET);
}

void LoraChangeMode(uint8_t mode)
{
	switch(mode)
	{
		case SLEEP:
			LoraWriteByte(RegOpMode,LoraReadByte(RegOpMode) & ~((1 << 2) | (1 << 1) | (1 << 0)) ); // reset bits 2, 1 and 0
			break;
		case STANDBY:
			LoraWriteByte(RegOpMode,LoraReadByte(RegOpMode) & ~((1 << 2) | (1 << 1))); // reset bits 2 and 1
			LoraWriteByte(RegOpMode,LoraReadByte(RegOpMode) | (1 << 0)); // set bit 0
			break;
		case TX:
			LoraWriteByte(RegOpMode,LoraReadByte(RegOpMode) & ~(1 << 2)); //reset bit 2
			LoraWriteByte(RegOpMode,LoraReadByte(RegOpMode) | (1 << 1) | (1 << 0)); // set bits 1 and 0
			break;
		case RXSINGLE:
			LoraWriteByte(RegOpMode,LoraReadByte(RegOpMode) & ~(1 << 0)); //reset bit 0
			LoraWriteByte(RegOpMode,LoraReadByte(RegOpMode) | (1 << 2) | (1 << 1)); // set bits 2 and 1
			break;
		case RXCONTINUOUS:
			LoraWriteByte(RegOpMode,LoraReadByte(RegOpMode) | (1 << 2) | (1 << 0)); // set bits 2 and 0
			LoraWriteByte(RegOpMode,LoraReadByte(RegOpMode) & ~(1 << 1)); //reset bit 0
			break;
		default:
			printf("Undefined Mode");
			break;

	}
}

void LoraSetXO(uint8_t state)
{
	if(state == SET)
	{
		//HAL_GPIO_WritePin(TCXO_GPIO_Port,TCXO_Pin,SET);
		HAL_Delay(5);
	}
	else if(state == RESET)
	{
		//HAL_GPIO_WritePin(TCXO_GPIO_Port,TCXO_Pin,RESET);
	}
}

void LoraSwitchModem(uint8_t modem) //0 for FSK/OOK, 1 for Lora
{
	if(modem == 0)
	{
		LoraWriteByte(RegOpMode, LoraReadByte(RegOpMode) & ~(1 << 7));
	}
	else if(modem == 1)
	{
		LoraWriteByte(RegOpMode, LoraReadByte(RegOpMode) | (1 << 7));
	}
	else
	{
		printf("Incorrect modem");
	}
}

void LoraReset()
{
    /*GPIO_InitTypeDef initStruct = { 0 };

    initStruct.Mode =GPIO_MODE_OUTPUT_PP;
    initStruct.Pull = GPIO_NOPULL;
    initStruct.Speed = GPIO_SPEED_HIGH;
    initStruct.Pin = RST_Pin;

    // Set RESET pin to 0
    HAL_GPIO_Init(RST_GPIO_Port,&initStruct);
    HAL_GPIO_WritePin( RST_GPIO_Port, RST_Pin, RESET );

    // Wait 1 ms
    HAL_Delay(1);

    // Configure RESET as input
    initStruct.Mode = GPIO_NOPULL;
    initStruct.Pin = RST_Pin;
    HAL_GPIO_Init( RST_GPIO_Port, &initStruct );

    // Wait 6 ms
    HAL_Delay(6);
    */
}

void LoraInit()
{
	HAL_Delay(10);

	LoraChangeMode(SLEEP);
	printUART(LoraReadByte(RegOpMode));
	LoraSwitchModem(LORA);
	HAL_Delay(5);
	printUART(LoraReadByte(RegOpMode));
	LoraChangeMode(STANDBY);
	HAL_Delay(5);
	printUART(LoraReadByte(RegOpMode));

	LoraSetChannel(868); //Radio Freq is 868 MHz

	LoraWriteByte(RegPaConfig,0x4F);
	LoraWriteByte(RegHopPeriod,0x00); //disable freq hopping
	LoraWriteByte(RegModemConfig1,0x73); // 01110011 -> Bw = 125KHz, CodingRate = 4/5, Mode = Implicit
	LoraWriteByte(RegModemConfig2,0x70); // 01110000 -> SpreadingFactor = 7, TxMode = Normal, CRC Disabled
	LoraWriteByte(RegModemConfig3,0x00);
	LoraWriteByte(RegPreambleMsb, 0x00);
	LoraWriteByte(RegPreambleLsb, 0x80); //preamble is 128 symbols
	LoraWriteByte(RegSymTimeoutLsb,0x64); //RXTIMEOUT after 100 symbols
	LoraWriteByte(RegPayloadLength,0x1); //Payload Length is always 1 byte because Mode is Implicit
	LoraWriteByte(RegDetectOptimize, (LoraReadByte(RegDetectOptimize) & 0xF8) | (0x03));
	LoraWriteByte(RegDetectionThreshold, 0x0A);
	LoraWriteByte(RegInvertIQ, (LoraReadByte(RegInvertIQ) & ~(0x41)));
	LoraWriteByte(RegInvertIQ2, 0x1D);
	LoraWriteByte(IfFreq2, 0x00);
	LoraWriteByte(IfFreq1, 0x40);

	printUART(LoraReadByte(RegOpMode));
}

void LoraSetChannel(uint32_t freq) //freq in Mhz, for example 868
{
	uint32_t channel = (freq * 524288)/ 32; //for 32MHz oscillator

	LoraWriteByte(RegFrMsb,(uint8_t) (channel >> 16));
	LoraWriteByte(RegFrMid, (uint8_t) (channel >> 8));
	LoraWriteByte(RegFrLsb, (uint8_t) channel);
}

void LoraTx(uint8_t byte)
{
	LoraChangeMode(STANDBY); // switch to STANDBY mode
	LoraWriteByte(RegFifoAddrPtr, LoraReadByte(RegFifoTxBaseAddr)); // set FifoPtrAddr to FifoTxBaseAddr
	LoraWriteByte(RegFifo,byte); //write Data to FIFO
	LoraChangeMode(TX); // switch to Tx Mode
	while( (LoraReadByte(RegIrqFlags) & (1 << 3)) == 0); // while is broken when TxDone is set
	LoraWriteByte( RegIrqFlags, (LoraReadByte(RegIrqFlags) | (1 << 3) ) ); //set TxDone to clear it
	printf("Tx Done\r\n");
}

uint8_t LoraRx()
{
	LoraChangeMode(STANDBY); // switch to STANDBY mode
	printUART(LoraReadByte(RegOpMode));
	LoraWriteByte(RegFifoAddrPtr, LoraReadByte(RegFifoRxBaseAddr)); // set FifoPtrAddr to FifoRxBaseAddr
	LoraChangeMode(RXSINGLE); // switch to RxSingle Mode
	//HAL_Delay(5);
	printUART(LoraReadByte(RegOpMode));

	while( ((LoraReadByte(RegIrqFlags) & (1 << 6)) == 0) && ((LoraReadByte(RegIrqFlags) & (1 << 7)) == 0) ) // while is broken when RxDone or RxTimeout is set
	{
		//printUART(LoraReadByte(RegOpMode));
		HAL_Delay(5);
	}


	if(LoraReadByte(RegIrqFlags) & (1 << 6))
	{
		LoraWriteByte( RegIrqFlags, (LoraReadByte(RegIrqFlags) | (1 << 6) )); //set RxDone and RxTimeout to clear it
		printf("RxDone\r\n");
		return 1;
	}
	else if(LoraReadByte(RegIrqFlags) & (1 << 7))
	{
		LoraWriteByte( RegIrqFlags, (LoraReadByte(RegIrqFlags) | (1 << 7) )); //set RxDone and RxTimeout to clear it
		printf("Rx Timeout\r\n");
		return 0;
	}
	else
	{
		return 2; //should not happen
	}



}

void printPayload()
{
	uint8_t payload;
	LoraWriteByte(RegFifoAddrPtr, LoraReadByte(RegFifoRxCurrentAddr));
	payload = LoraReadByte(RegFifo);
	printUART(payload);
}
/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{ 
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

That code does not appear to be related to TTN.