CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

I2C Bootloader PIC16
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
bastirn



Joined: 08 Jan 2020
Posts: 17

View user's profile Send private message

I2C Bootloader PIC16
PostPosted: Thu Jun 18, 2020 10:32 am     Reply with quote

Hello, I am using a PIC16LF1829 and ccs PCM V5.090.
I have an application which use I2C master mode.
I am trying to add with this app a bootloader which use the same I2C bus but in slave mode.

Is it even possible too realize this configuration ?
Should i need to reset the cpu to go from the app to the bootloader ?

I used the i2c bootloader example of ccs to do my program.

Best regards
Bastien
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Thu Jun 18, 2020 11:41 am     Reply with quote

Yes. I would though be slightly 'sneaky' to avoid any potential problems.

In the bootloader, change the I2C setup, add the statement 'NOINIT' to
the #USE I2C. This then leaves the I2C uninitialised at boot.

Then in the code _after_ the point where it has decided it wants to
actually 'bootload' (I assume you are using a switch or something to
trigger the decision to bootload?). Add the statement 'I2C_init();' this
will at this point configure the I2C as a slave as setup in the bootloader
#USE i2C.

The master in the main code can just be setup as normal.

So the sequence becomes that the chip boots, does not send any
configuration to the I2C, but looks at the input and decides whether
to bootload, or run the application. If it decides to bootload, it'll
call the code to initialise the I2C as a slave, and then proceed to
load the code.
In the main, it'll arrive with the I2C 'uninitialised', so the standard
code will all run fine.

Now this will obviously work fine with a reset, but also the bootloader
could call the cpu reset instruction after loading. Again this would leave
the hardware in a reset condition to ensure there are no problems.
bastirn



Joined: 08 Jan 2020
Posts: 17

View user's profile Send private message

PostPosted: Fri Jun 19, 2020 3:01 am     Reply with quote

Ok i did some modification to my program, but the problem is that i can't go to bootloader from the application. When i use my UART command to launch bootloader my PIC crash. My trigger is the rs232 command "bootloader on/r".

In addition to my I2C bus i have a rs232 link which i enable in bootloader to do some debug. The bootloader rs232 config is exactly the same as the application one. When i disable it in the bootloader there is no change on the working, so i assume that the problem is somewhere else.

When i use the command to launch bootloader i assume that the PIC goes to somewhere which is not the bootloader main.

Nothing weird in the .lst file which make me think it is a missing goto or something like that.

Here is some part of my code which do the config and the switch between the bootloader and the application.

Bootloader initialisation :
Code:
#include <16LF1829.h>

#device PASS_STRINGS = IN_RAM   // important pour la liaison série

#define INT_I2C_BOOTLOADER  INT_SSP

#define SDA1_PIN            PIN_B4          // SDA I2C1
#define SCL1_PIN            PIN_B6          // SCL I2C1

#define TX_PIN              PIN_C4          // Pin TX RS232
#define RX_PIN              PIN_C5          // Pin RX RS232

#use delay (clock=16M)

#use i2c(SLAVE, SDA=SDA1_PIN, SCL=SCL1_PIN, address=0xA0, STREAM=STREAM_I2C_BOOTLOADER, FORCE_HW, NOINIT)
#use rs232(baud=115200, xmit=TX_PIN, rcv=RX_PIN, stream=PC_BOOTLOAD, ERRORS)

#define __bootloader    //tell i2c_bootloader.h that this is the bootloader program

#include "rom.c"
#include "i2c_bootloader.h"
int bootloader_enabler=0;


Bootloader main :
Code:

void main(void)
{
    if (bootloader_enabler==0){
        bootloader_enabler=1;
        printf("APPLICATION STARTED\r\n");
        i2c_bootloader_goto_application(); // provided in ccs ex_ldr_bootloader.c
    } else {
        bootloader_enabler=0;
        I2C_init(STREAM_I2C_BOOTLOADER,TRUE);   //switch on the slave peripheral
        printf("BOOTLOADER STARTED\r\n");
        i2c_bootloader_loop(); // provided in ccs ex_ldr_bootloader.c
    }
}


Application initialisation :
Code:
#include <16LF1829.h>

#device PASS_STRINGS = IN_RAM   // important pour la liaison série
#device adc=10                  // adc 10 bits

#fuses INTRC_IO
#fuses WDT
#fuses NOPROTECT
#fuses NOLVP
#fuses NOBROWNOUT

#USE STANDARD_IO(a)
#USE STANDARD_IO(b)
#USE STANDARD_IO(c)

#use delay (clock=16M)

/* Setup I2C */
#use i2c (MULTI_MASTER, SDA=SDA1_PIN, SCL=SCL1_PIN, FORCE_HW, NO_STRETCH, stream=MASTER)

// Liaison série
#use rs232(baud=115200, xmit=TX_PIN, rcv=RX_PIN, stream=PC, ERRORS)


The application code which launch the bootloader :
Code:
        printf(TXT_CMD_BOOTLOADER_ON);
        printf(MAIN_TXT_INDENTATION);
        I2C_init(MASTER,FALSE);     //switch off the master i2c
        disable_interrupts(GLOBAL);
        #asm
        GOTO PROGRAM_BOOTLOADER_START
        #endasm


I also used the file i2c_bootloader.h provided by CCS. It is the same content except that my bootloader start at 0x0000, the loader size is 0x03FF, and i include my own .hex. Here are some part that i changed :
Code:
#ifndef PROGROM_LOADER_SIZE
#define PROGROM_LOADER_SIZE   0x03FF
#endif

#define PROGRAM_BOOTLOADER_START 0x0000
#define PROGROM_APP_START  (PROGRAM_BOOTLOADER_START + PROGROM_LOADER_SIZE + 1)
#define PROGROM_ISR_START  (PROGROM_APP_START + 4)
#ifndef __bootloader            // si c'est l'application qui charge ce fichier

    #build(reset=PROGROM_APP_START, interrupt=PROGROM_ISR_START)
    #org 0, PROGROM_LOADER_SIZE {}
    #IGNORE_WARNINGS 228

    // import du bootloader dans l'espace mêmoire réservée (à commenter pour compiler uniquement l'application)
    #import(HEX, file="build_bootloader.hex", RANGE=0:PROGROM_LOADER_SIZE)

#else   // si c'est le bootloader qui chage ce fichier

    #org PROGROM_ISR_START+0x10, (getenv("PROGRAM_MEMORY")-1) {}
    #org PROGROM_APP_START, PROGROM_APP_START+0xF
 


Thank you in advance for any answer.
Best regards,

Bastien
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Fri Jun 19, 2020 3:52 am     Reply with quote

Uurgh. That is the opposite of any normal usage. You don't really have
a 'bootloader' if you are calling it from the application. The keyword is
'boot' the idea is that the bootloader is executed at boot (so before
the application loads).
There are significant issues with what you post. Most obvious is the use
of 'bootloader_enabler'. The variable having this name in the main code,
will not be the same as the variable in the bootloader code. Since it is
set to zero in the initialisation of the bootloader, it is always going to be
0 when the bootloader tests it. If you want a shared variable to flag between
the routines, you need to #locate both variables to the same address, and
only initialise it when 'restart_cause' says you are doing a normal boot.
bastirn



Joined: 08 Jan 2020
Posts: 17

View user's profile Send private message

PostPosted: Fri Jun 19, 2020 4:26 am     Reply with quote

Thank you for your answer,
I didn't explain how my card is working so here it is :

I have a PMIC which provide alimentation for my PIC but it need configuration to provide alimentation for a CPU which allow the system to work.

It imply that i have to do some i2c programming stuff and toggle some pin to be able to boot the CPU. If i load the bootloader at the start without programing the PMIC, the card will not boot and i will not be able to bootload the PIC with the CPU.

For the variable :
The 'bootloader_enabler' variable is only defined and modified in the bootloader code part. If i started the application from the bootloader part i will next time i pass in the main loop of the bootloader, go to the bootloader code. I don't know if its possible but its what i tried.

I hope we will find a way to do it ...

Best regards
Bastien
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Fri Jun 19, 2020 6:15 am     Reply with quote

You are missing the point here.
Bootloader_enabler, is set to zero at the start of the bootloader, so will
always be zero. So the bootloader can never be called, since if this is
zero, the code jumps to the main. A variable that is set to a value like
this is set to the value by the code at the start of the main....
Code:

void main(void)
{
    //bootloader_enabler is always going to be 0 at this point....
    if (bootloader_enabler==0){
        bootloader_enabler=1;
        printf("APPLICATION STARTED\r\n");
        //so the code is always going to go to the application...
        i2c_bootloader_goto_application(); // provided in ccs ex_ldr_bootloader.c
    } else {
        bootloader_enabler=0;
        I2C_init(STREAM_I2C_BOOTLOADER,TRUE);   //switch on the slave peripheral
        printf("BOOTLOADER STARTED\r\n");
        i2c_bootloader_loop(); // provided in ccs ex_ldr_bootloader.c
    }
}

int bootloader_enabler=0;

The code to set this to 0, is part of the start of the 'main'....

You need to change things like this:

First have bootloader_enabler declared as:

int bootloader_enabler; //No initialisation

Then have your bootloader main do this:
Code:

void main(void)
{
    if (restart_cause()==NORMAL_POWER_UP)
        bootloader_enabler=0; //This only clears on power on
    if (bootloader_enabler==0){
        bootloader_enabler=1;
        printf("APPLICATION STARTED\r\n");
        //so the code is always going to go to the application...
        i2c_bootloader_goto_application(); // provided in ccs ex_ldr_bootloader.c
    } else {
        bootloader_enabler=0;
        I2C_init(STREAM_I2C_BOOTLOADER,TRUE);   //switch on the slave peripheral
        printf("BOOTLOADER STARTED\r\n");
        i2c_bootloader_loop(); // provided in ccs ex_ldr_bootloader.c
    }
}


This way when you reboot, the enabler can still be set.

However there is a second issue here. The memory that is used by this
variable needs to be protected, otherwise when your main application
runs it can/will be overwritten. The main code will use exactly the same
RAM as the booltoader for it's variables. So you need to add code to the
main to _not_ use the RAM area that holds this variable. Otherwise
it may well be overwritten....

So, after compiling the bootloader, open the .sym file, and find where
this variable is stored. Then in your main application add a #RESERVE
statement to protect this RAM area.
bastirn



Joined: 08 Jan 2020
Posts: 17

View user's profile Send private message

PostPosted: Mon Jun 22, 2020 3:32 am     Reply with quote

Hello,
Thank you very much for your advice, I follow your instruction but i always have the same error.

When i send my command from UART to start the bootloader the PIC stuck after sending me this line. (On app side) :
Code:
 printf(TXT_CMD_BOOTLOADER_ON);
        printf(MAIN_TXT_INDENTATION);


But it also print a line that is not expected (marked with a comment).

Code:

} else if (strncmp(command, TXT_CMD_TRACK_WRITE_T30, TXT_LEN_TRACK_WRITE_T30) == 0) {
        int8 len_command = strlen(command);
        char str_value[TRACK_SIZE] = "";

        if (len_command > (TXT_LEN_TRACK_WRITE_T30 + 1 +TRACK_SIZE) || (len_command < TXT_LEN_TRACK_WRITE_T30 + 2)) {
            printf(TXT_TRACK_WRITE_ERROR, TRACK_SIZE);
        } else {
            // récupération des caractères saisie par l'utilisateur
            for (i = TXT_LEN_TRACK_WRITE_T30 + 1;i < len_command;i++) {
                strncat(str_value, &command[i], 1);   
            }

            if (track_write(str_value, CODE_TRACK_ADDR_T30) == FALSE) {
                printf(TXT_TRACK_WRITE_ERROR, TRACK_SIZE); // THIS LINE IS PRINTED WHILE I TRY TO LAUNCH BOOTLOADER
            }
        }

...
Other else if function for the UART communication
...

    } else if (strcmp(command, TXT_CMD_BOOTLOADER_ON) == 0) {
        printf(TXT_CMD_BOOTLOADER_ON);
        printf(MAIN_TXT_INDENTATION);
        I2C_init(MASTER,FALSE);     //switch on the slave peripheral
        disable_interrupts(GLOBAL);
        #asm
        GOTO PROGRAM_BOOTLOADER_START
        #endasm

    } else {
        printf(TXT_UNKNOWN_COMMAND);
    }


The PIC doesn't print anything on the RS232 after this.
I don't really know what the PIC is doing after this, he just keep working as configured in the application program. I try to toggle a GPIO in the bootloader but it doesn't work. Also, the PIC doesn't reset so i guess that the hardware watchdog is refreshed.
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Mon Jun 22, 2020 6:03 am     Reply with quote

It's impossible to see what you really 'have', since so much is in defines,
but as posted, the 'error' part will be reached, if
TXT_CMD_TRACK_WRITE_T30 has been matched, and the length test
is not met.
You really need to sit down and get your string tests working right before
actually trying to trigger the bootloader. Also, you don't need to use:
Code:

        #asm
        GOTO PROGRAM_BOOTLOADER_START
        #endasm

Since it is a 'bootloader', just use:

reset_cpu();

If the flag is set, this will result in the bootloader being called.

There is a major 'big issue' too, if you are using the watchdog.
Your bootloader code is going to have to keep this reset while it
receives data. But you will be defaulting to a speed that is too fast
for the memory write to actually be done.

I'd suggest you change the watchdog fuse to WDT_SW, and then have
a setup_wdt(WDT_ON); near the start of your main code.
Then have setup_wdt(WDT_OFF) before your reset to launch the
bootloader.
This way you avoid the problems that this will otherwise cause....

Having a watchdog enabled while performing flash memory writes
is 'asking for trouble'... Sad
bastirn



Joined: 08 Jan 2020
Posts: 17

View user's profile Send private message

PostPosted: Mon Jun 22, 2020 8:44 am     Reply with quote

Thank you, I fix the watchdog as you suggest.

The problem is that i can't reset the PIC. The PIC is holding the CPU reset pin, if the PIC reboot the card will reboot. Consequently If the PIC didn't load the application I'am stuck with nothing boot except the PIC in bootloader mode. I need to go to the bootloader from the app and keep some GPIO under control.

Do you think it is possible ?
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Mon Jun 22, 2020 11:14 am     Reply with quote

Just use the command

reset_cpu();

This makes the CPU reset. No I/O pin needed.
bastirn



Joined: 08 Jan 2020
Posts: 17

View user's profile Send private message

PostPosted: Mon Jun 22, 2020 11:51 am     Reply with quote

I think we do not understand each other.

When I use this command the PIC reset but also my card. I need to keep the system working. i/o must stay at the same state defined in the app when launching the bootloader. If i reset the PIC, i/o state will change and reset the entire system.

By the way, i have another way to bypass the reset mandatory problem to go to bootloader. It is to make the bootloader able to start the system. So i am trying to do a function which will be defined in the application code but called from the bootloader. This function will speak on i2c bus + drive some i/o. Is it possible ?
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Mon Jun 22, 2020 12:52 pm     Reply with quote

Get rid of the idea of using a 'bootloader'.
Just have a 'loader' (nothing to do with 'boot'), that occupies a protected
block somewhere in memory. Call this from your main code.
Forget about switching flags for different boots etc.. Just have the loader
perhaps occupy the top 0x400 bytes of ROM, and jump to this when
you want to load.
Understand that a bootloader, implies the system is _booting_. As such
all peripherals will be in their 'reset' state. Write the loader so it won't
write to it's own area of memory, so that you can just compile the main
code with this included, and then in future it doesn't get replaced.
So just a loader application, in a fixed location in the memory, that won't
overwrite itself.

It's worth understanding, that the reason for using a 'bootloader', is that
with this, the loader actually 'boots'. There at the start. It also triggers
using either an external switch, or some other signal that can ideally be
sent without the main application every having to start. Done like this,
if something goes wrong, and you load code that doesn't run correctly,
you can always reprogram with the bootloader.
Now in your case, you are getting rid of the ability of the bootloader to
run on the bare machine at the start, instead requiring the application
to call it. This is potentially dangerous, since if something then goes wrong
with the application code, then the loader cannot be called. However if
you are prepared to accept this risk, then the need for a 'bootloader'
disappears.
Personally, I'd want to get rid of the need for the application to actually
be involved. I'd want the bootloader to set whatever pins are needed to
allow the processor to boot, and to be able to be triggered to load
without the application. A much better answer really. I'm sure this could be
done. Something like sending a single character on the serial within a
few seconds of boot for example, or Holding the I2C clock line low
during the boot, and having the code check this, and if found, then waiting
till it is released, and starting the bootload operation. This gives no extra
pins needed.
bastirn



Joined: 08 Jan 2020
Posts: 17

View user's profile Send private message

PostPosted: Thu Jul 02, 2020 5:33 am     Reply with quote

I made my bootloader able to start the card. It's working well but now i have another problem.
When i try to modify the rom content with the bootloader command i only replace 8bits in the opcode (instead of 14bits).
I used the i2c_bootloader.h, i2c_bootloader.c, and rom_write.c as provided by ccs.
To modify rom content i use linux to send i2c data.

Example :
Initial situation :

ADDRESS 1122 : OPCODE 0203
ADDRESS 1123 : OPCODE 0304

(0x50 = PIC i2c adress)
First I send the command : i2cset -y 1 0x50 0x01 0x22 0x11 0x00 0x00
And after i send this : i2cset -y 1 0x50 0x02 0x0FFF w
(I also tried this : i2cset -y 1 0x50 0x02 0x0F 0xFF i)

I use the icd3 to see if program memory is changed and i obtain this:

ADDRESS 1122 : OPCODE 02FF
ADDRESS 1123 : OPCODE 030F

and i wanted that :

ADDRESS 1122 : OPCODE 0FFF

Is there something to modify in the ccs program ?

Thank in advance for any answer.
Best regards
Bastien
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Thu Jul 02, 2020 6:42 am     Reply with quote

What format are you generating the application object file in?.
You get that behaviour if this is set wrong.
bastirn



Joined: 08 Jan 2020
Posts: 17

View user's profile Send private message

PostPosted: Thu Jul 02, 2020 6:48 am     Reply with quote

I use this line to compile my program :
"C:\Program Files (x86)\PICC\ccsc" +FM +Y9 +EA +PE build_bootloader.c
"C:\Program Files (x86)\PICC\ccsc" +FM +Y9 +EA +PE build.c

It makes a .hex file which i load in the PIC using icd3 (i can also use a load-n-go).
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group