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.

CC253X CC2540 (CC2530) going into Power mode

Other Parts Discussed in Thread: CC2530, CC2591, TMP103

Hi!

The datasheet/User guide SWRU191B says...


 The Instruction that sets the PCON.IDLE bit must be aligned in a certain way for correct operation. The first byte of the assembly instruction immediately following this instruction must not be placed on a 4-byte boundary. Furthermore, cache must not be disabled (see CM in the FCTL register description in chapter 6). Failure to comply with this requirement may cause higher current consumption. Provided this requirement is fulfilled, the first assembly instruction after the instruction that sets the PCON.IDLE bit is performed before the ISR of the interrupt that caused the system to wake up, but after the system woke up. If this instruction is a global interrupt disable, it is possible to have it followed by code for execution after wakeup, but before the ISR is serviced.

I cannot understand how to exactly realise this  with my c code. And since I am not using IAR, I am on Linux and using sdcc compiler to compile my CC2530 software. I have done the following in my main.c (Still developing and not completed project):

...
#define GO_SLEEP (PCON |= 0x0E)
...

void isr_tamper(void) interrupt (P0INT_VECTOR){//Tamper pin interrupt vector, which wakes up the CPU

P0IE = 0;

EA = 0;

//do something

uart_tx("katkestus!");

//useful here

P0IF = 0;

P0IFG = ~_BV(6);

EA = 1;

P0IE = 1;

}

...

void init_sleep_mode(char a[]){

FCTL |= 0x0C;//Flash cache enabled, real-time mode

if(a == "PM3"){

SLEEPCMD |= _BV(0);//PM3

SLEEPCMD |= _BV(1);}

if(a == "PM2"){

SLEEPCMD &= ~_BV(0);//PM2

SLEEPCMD |= _BV(1);}

if(a == "PM1"){

SLEEPCMD |= _BV(0);//PM1

SLEEPCMD &= ~_BV(1);}

}

...

void go_sleep(void){/* Enter Sleep mode defined by SLEEPCMD*/

__asm;

//Can somebody tell me what i have to exactly do here?

__endasm;

...

void main(void){

init_sleep_mode("PM2");

GO_SLEEP;

.... //?????????????

....

}

As u can see in init_sleep_mode() the cache is also enabled as said in datasheet...
Any suggestions? Because right now i can minimize the current down to 4.04 mA only :/ Mainly i cannot understand how to put those instructions out/into 4 byte boundaries... 

Thank you! 

 

  • I'm not familiar with the sdcc compiler but it should have some documentation somewhere explaining how it implements the C language.  This document should also contain such things as compiler options and pragma options which it understands.  I suspec there is a pragma option to force alignment of code to a particular boundary.  If not, you may need to turn on an option to keep generated assembly files and then modify the generated assembly file to place a .ALIGN or some similar assembly directrive to force proper alignment.  Then you would merely use this modified assembly file for your code and not the C file (you may want to keep it for reference though).  You may also want to create a seperate C file with just this function in it so you can easily modify other functions and not have to recreate this assembly file over and over.

    Jim Noxon

  • Hi,

    You have to write it in assembler: go_sleep.S

    PCON = 0x87;   Power Mode Control Register

    .MODULE go_to_sleep
    .GLOBL _go_sleep

    .AREA HOME

    ; NOP                                          ; align to 2-byte boundary (even location)

    _go_sleep:                                ; void go_sleep(void) {

      MOV PCON, #0x01                ;   PCON = 0x01;    // halts CPU

      NOP                                          ;   NOP();
      RET                                          ; }

     

    In your Makefile:

    AS       = sdas8051
    ASFLAGS = --plosgff

    %.rel: %.S

      $(AS) $(ASFLAGS)  -o $@ $<

     

    Verify in the .map file the address of _go_sleep. If it's odd, uncomment the nop instruction before _go_sleep and re-check the .map file to make sure it's even.

     

    Hope this helps.

    Zafi.

  • Okay, thank you both.

    I could not find such pragma, but i tried to manually align the procedure placement then, because 
    I did not thoroughly understand what Zafi wrote (I do not have much experience writing assembly)

    My main.map file:

    ..................
     Hexadecimal


    Area                               Addr   Size   Decimal Bytes (Attributes)

    --------------------------------   ----   ----   ------- ----- ------------

    CSEG                               00E9   5D34 =  23860. bytes (REL,CON,CODE)


          Value  Global

       --------  --------------------------------

      0C:00E9    _isr_tamper

      0C:00F4    _init_tamper_interrupt

      0C:011A    _init_sleep_mode

      0C:018C    _go_sleep

      0C:019C    _main

      0C:04B2    _zb_adhoco_recv_value

    ...........

    018C % 4 = 0  Okay?

    So in my C file i did:

    .......

    void init_sleep_mode(char a[]){

    FCTL |= 0x04;//Flash cache enabled

    if(a == "PM3"){

    SLEEPCMD |= _BV(0);//PM3

    SLEEPCMD |= _BV(1);}

    if(a == "PM2"){

    SLEEPCMD &= ~_BV(0);//PM2

    SLEEPCMD |= _BV(1);}

    if(a == "PM1"){

    SLEEPCMD |= _BV(0);//PM1

    SLEEPCMD &= ~_BV(1);}

    __asm;

    NOP

    NOP

    NOP

    __endasm;

     

    }

     

    void go_sleep(void){ /* Enter Sleep mode defined by SLEEPCMD*/

    P1SEL &= ~_BV(4);

    P1SEL &= ~_BV(5);

    P1DIR |= _BV(4) | _BV(5);

    P1_4 = 0;

    P1_5 = 0;

    __asm;

    MOV PCON, #0x01

    NOP

    RET

    __endasm;

     

    }

    ............

    So everytime i changed main.c file i edited the .asm file to ensure the correct placement by adding NOPs at the end of init procedure...
    Is it the right way?
    Because i still measure 4 mA...

    My design is according to the AN086 on board with the range enhancer CC2591...
    any help is greatly welcome, because now my battery lasts only 24 hours with this system...

    Thank You! 

  • Hi Sven,

    The idea is to make sure that the instruction "MOV PCON, #0x01" (3-byte instruction)  is at an even address so that the NOP just after it is not on a 4-byte bondary.

    What I suggested is to write an assembler file (go_sleep.S)  containing the following instructions:


    PCON = 0x87             ;   Power Mode Control Register

    .MODULE go_to_sleep
    .GLOBL _go_sleep

    .AREA HOME

    ; NOP                                          ; align to 2-byte boundary (even location)

    _go_sleep:                                ; void go_sleep(void) {

      MOV PCON, #0x01                         ;   PCON = 0x01;    // halts CPU

      NOP                                          ;   NOP();
      RET                                          ; }

    MOV has the same address as _go_sleep. If _go_sleep has an odd address, by removing the comment ";" before the NOP instruction above, _go_sleep is at an even address.

    You can assemble this file and link it with your app in your makefile as per my pevious email (let me know if it's unclear).

    In your main.c, you just declare

    void go_sleep(void);

    and call it wherever you need. It should stay at the same address when you change your main.c (just double-check in case).

    Regards,

    Zafi.

  • Hi, Thank you again


    I cannot understand exactly what and where do i do and put these:

    %.rel: %.S

      $(AS) $(ASFLAGS)  -o $@ $<

    right now my makefile has rows:

    PROGRAM_NAME = device_zigbee_smoke_detector.bin

    CC = sdcc

    CFLAGS = --model-large --xram-loc 0x0000 -I. -I../lib/ -I../lib/smoke/

    AS       = sdas8051

    ASFLAGS = --plosgff


    all:

    $(CC) $(CFLAGS) -c main.c -o ../lib/tmp/main.rel

    $(CC) $(CFLAGS) -c ../lib/smoke/hw.c -o ../lib/tmp/hw.rel

    $(CC) $(CFLAGS) -c ../lib/i2c.c -o ../lib/tmp/i2c.rel

    $(CC) $(CFLAGS) -c ../lib/sx1502.c -o ../lib/tmp/sx1502.rel

    $(CC) $(CFLAGS) -c ../lib/tmp103.c -o ../lib/tmp/tmp103.rel

    $(CC) $(CFLAGS) -c ../lib/platform.c -o ../lib/tmp/platform.rel

    $(CC) $(CFLAGS) -c ../lib/flash.c -o ../lib/tmp/flash.rel

    $(CC) $(CFLAGS) -c ../lib/serial.c -o ../lib/tmp/serial.rel

    $(CC) $(CFLAGS) -c ../lib/mtimer.c -o ../lib/tmp/mtimer.rel

    $(CC) $(CFLAGS) -c ../lib/rf.c -o ../lib/tmp/rf.rel

    $(CC) $(CFLAGS) -c ../lib/rf_mac.c -o ../lib/tmp/rf_mac.rel

    $(CC) $(CFLAGS) -c ../lib/zb.c -o ../lib/tmp/zb.rel

    $(CC) $(CFLAGS) -c ../lib/zb_control.c -o ../lib/tmp/zb_control.rel

    $(CC) $(CFLAGS) ../lib/tmp/main.rel ../lib/tmp/platform.rel ../lib/tmp/flash.rel ../lib/tmp/serial.rel ../lib/tmp/mtimer.rel ../lib/tmp/rf.rel ../lib/tmp/rf_mac.rel ../lib/tmp/zb.rel ../lib/tmp/zb_control.rel ../lib/tmp/i2c.rel ../lib/tmp/sx1502.rel ../lib/tmp/tmp103.rel ../lib/tmp/hw.rel -o ../lib/tmp/main.ihx

    objcopy --gap-fill 0xFF -I ihex -O binary ../lib/tmp/main.ihx $(PROGRAM_NAME)

    chmod 755 $(PROGRAM_NAME)

  • Hi,

    I see. Add the following line :

    $(AS) $(ASFLAGS) -o ../lib/tmp/go_sleep.rel go_sleep.S

    and change the last $(CC) line to:

    $(CC) $(CFLAGS) ../lib/tmp/main.rel ../lib/tmp/platform.rel ../lib/tmp/flash.rel ../lib/tmp/serial.rel ../lib/tmp/mtimer.rel ../lib/tmp/rf.rel ../lib/tmp/rf_mac.rel ../lib/tmp/zb.rel ../lib/tmp/zb_control.rel ../lib/tmp/i2c.rel ../lib/tmp/sx1502.rel ../lib/tmp/tmp103.rel ../lib/tmp/hw.rel ../lib/tmp/go_sleep.rel -o ../lib/tmp/main.ihx

  • Thanks!

    I had to

    AS       = asx8051

    ASFLAGS = -plosgff 


    But the principal remains the same... 
    After all this doesn't change the outcome much, because there is something else on the circuit which sucks up the coulombs...