This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2650: CMD_RADIO_SETUP stuck in ACTIVE state

Other Parts Discussed in Thread: CC2650

Hello everyone!

I'm currently working on porting RIOT OS to the CC2650 sensortag. In the past few days I've been writing the drivers for the radio, starting with BLE. Unfortunately I appeared to have hit a wall when it comes to configuring the radio through the CMD_RADIO_SETUP command (ID: 0x0802).


After sending the radio operation command to the RF core, it gets stuck and the command status never change from ACTIVE (0x002). I'm rather puzzled because:

  • My code checks that the RF Core works properly by submitting both a PING direct command and a CMD_NOP radio operation command, both of which are handled successfully. Moreover I'm able to start the RAT using CMD_START_RAT.
  • I've compared the content of my command byte to byte with the implementation of Contiki OS and they are exactly the same, except for the pointer value. Here is my command, divided in bytes:
    [ 02 08 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 29 00 1c 00 00 20 ]

Any idea about what I'm missing? You can find my code on GitHub. Here is a summary of what I'm doing:

  1. Unlock clock gate for the RFC power domain
  2. Turn on the RFC power domain
  3. Turn on all available clocks in PWMCLKEN
  4. Enable interrupts
  5. Test the RF core by sending a PING and NOP command
  6. Turn on RAT timer using the direct command
  7. Send the RADIO_SETUP command (it fails here).

Any suggestion regarding BLE setup would be greatly appreciated. Thank you!

  • Hi,

    Can you check if the device has switched to XOSC_HF as sclk_hf source before you send out CMD_RADIO_SETUP?

    Also we do have RF driver which can be found on TI-RTOS version >= 2.15, can you try using RF driver provided by TI and see if you still have the same problem?
  • Changing the HF clock source to XOSC worked! Thank you SO much! :)
    Is this a requirement for all radio operations? I supposed the use of the high-accuracy crystal is necessary because of the frequency synthesizer?

  • yes, for all radio operation in CC26xx, the sclk_hf has to run off XOSCHF
  • Good to know! Thank you once again for your help!
  • Hi Christin,

    I have the same problem. But in my case, it still stucks on ACTIVE state. I have confirmed XOSC_HF is successfully enabled and switched since OSCHF_AttemptToSwitchToXosc() return true and the registers' value looks fine.

    What could be the reason?
  • Hi,

    What's your HW and SW(TI-RTOS) version? can you post code snippet?
  • I use my own RTOS on custom board. My plan is to develop my own BLE stack based on my RTOS and C++. First, I enable XOSCHF in main() 

    OSCHF_TurnOnXosc();
    if (!OSCHF_AttemptToSwitchToXosc())
    	while(1);

    I define a RfcBle class which inherits RFC class. The RFC class will handle basic RFC operations while RfcBle handles BLE specific radio operations. When a RfcBle instance is opened, it will execute RfcBle::open which will call RFC::open() at first:

    void RFC::open()
    {
    	// Disable RF core in case it was enabled
    	prcm_disable_module(MOD_RFC);
    
        // Set RF mode
        // 	IMPORTANT: this register is _very_ poorly documented, but:
        //            	- you need to set it while the RF CPU is powered down
        //            	- without properly setting it, you'll get 0x82 (unknown command) for any protocol-specific commands
        //            	- it is unclear which RFC mode corresponds to which RF protocol
        //            	- setting it to "mode 1" will make BLE work on the CC2640
        //
        // See:
        // 	e2e.ti.com/.../420672
        // 	e2e.ti.com/.../491766
        // 	e2e.ti.com/.../1889768
        // 	e2e.ti.com/.../1895357
        // 	e2e.ti.com/.../519960
    	reg_PRCM.RFCMODESEL = 1;
    
    	// Enable RF core
    	prcm_enable_module(MOD_RFC);
    
    	pos_sema_init(&rfc_cmd_sema, 0);
    
    	// Select event for CPE0/CPE1 interrupts (Table 23-107)
    	// CPE = RF Command and Packet Engine
    	reg_RFC_DBELL.RFCPEISL = 0;
    	reg_RFC_DBELL.RFCPEIEN = RFCPE_CMD_DONE | RFCPE_LAST_CMD_DONE
    							| RFCPE_TX_DONE | RFCPE_TX_ACK | RFCPE_TX_CTRL | RFCPE_TX_CTRL_ACK | RFCPE_TX_CTRL_ACK_ACK | RFCPE_TX_RETRANS | RFCPE_TX_ENTRY_DONE | RFCPE_TX_BUFFER_CHANGED
    							| RFCPE_RX_OK | RFCPE_RX_NOK | RFCPE_RX_IGNORED | RFCPE_RX_EMPTY | RFCPE_RX_CTRL | RFCPE_RX_CTRL_ACK | RFCPE_RX_BUF_FULL | RFCPE_RX_ENTRY_DONE
    							| RFCPE_MODULES_UNLOCKED | RFCPE_BOOT_DONE | RFCPE_INTERNAL_ERROR;
    	pos_set_handler(ISRNO_RF_CPE0, rfc_cpe0_isr);
    
    	// TI does not recommend servicing HW interrupts in the main CPU,
    	// but the available radio timer channel interrupts may be served this way.
    	reg_RFC_DBELL.RFHWIEN = 0;
    
    	pos_set_handler(ISRNO_RF_CMD_ACK, rfc_cmd_ack_isr);
    
        // Enablde RF clocks
    	// As soon as the RF core is started it will handle clock control autonomously.
    	reg_RFC_PWR.PWMCLKEN = RFC_PWR_ALL;
    //	reg_RFC_PWR.PWMCLKEN = RFC_PWR_RFC|RFC_PWR_CPE|RFC_PWR_CPERAM;
    
    	while (1) {
    		if (reg_RFC_DBELL.RFCPEIFG & RFCPE_BOOT_DONE)
    			break;
    	}
    }

    Here is RfcBle::open().

    bool RfcBle::open()
    {
    	RFC::open();
    
    	// Setup the RF core for use with BLE
    	__attribute__((aligned(4)))
    	rfc_CMD_RADIO_SETUP_t cmd;
    	memset(&cmd, 0, sizeof(rfc_CMD_RADIO_SETUP_t));
    
    	cmd.radioOp.commandNo = CMD_RADIO_SETUP;
    	cmd.radioOp.condition.rule = COND_NEVER;
    	cmd.mode = RF_MODE_BLE;
    	cmd.config.frontEndMode = RF_FE_MODE;
    	cmd.config.biasMode = RF_FE_BIAS;
    //	cmd.config.analogCfgMode = 0x2D;
    	cmd.txPower = getTxPowerValue(TX_POWER_0_DBM);
    	cmd.pRegOverride = rfc_regOverrides;
    
    	int rc = sendCmd((rfc_radioOp_t*)&cmd, 1000);
    	if (rc != STA_DONE_OK)
    		return false;
    
    	// Start the radio timer
    	// The radio timer must be running to run a radio operation command with delayed start
    	// or any radio operation command that runs the receiver or transmitter.
        rc = sendDirectCmd(CMD_START_RAT, 0, 0);
        if (rc != ACK_DONE && rc != ACK_CONTEXT_ERROR)    // ContextError means RAT already running
        	return false;
    
        isOpened = true;
        return true;
    }

    It is hard to list all used function calls. But you can understand their meaning by their names. The function sendCmd() is defined as below:

    int RFC::sendCmd(rfc_radioOp_t* cmd, int timeout)
    {
    	// Three classes of commands:
    	// 1. Radio operation command
    	// 2. Immediate command
    	// 3. Direct command
    	// For 1&2, CMDR is a pointer to the command structure with 4 bytes aligned.
    	// For 3, the direct command is signaled by setting the 2 LSBs to 01.
    	// When the system CPU prepares a command structure, the CPU must initialize the status field to Idle.
    
    	// Wait for CMDR to be zero
    	while (reg_RFC_DBELL.CMDR != 0);
    
    //	clearCpeIntFlags();
    
    	// Set the command
    	reg_RFC_DBELL.CMDR = (uint32_t)cmd;
    
    	// Wait for RFCMDACK interrupt
    	if (!pos_sema_pend(&rfc_cmd_sema, timeout))
    		return -1;
    
    //	clearCpeIntFlags();
    
    	// Return error immediately
    	int sta = reg_RFC_DBELL.CMDSTA;
    	if (sta > 1)
    		return sta;
    
    	// Wait for command to complete
    	// i.e. wait when STA_IDLE, STA_PENDING or STA_ACTIVE
    	while (cmd->status <= STA_ACTIVE);
    
    	// Return command status
    	return cmd->status;
    }

    When executing, it blocks at the line "while (cmd->status <= STA_ACTIVE);". In watch window, the status is 0x0002 which is defined as STA_ACTIVE.