Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Allow user to upload a "light show routine" to PIC, without coding?

Status
Not open for further replies.

Triode

Well-Known Member
I made a light show controller for some friends, currently to reprogram it I have to edit the PIC code and upload it for them. I can make software to design the show on a computer, I'm just trying to figure out the least cumbersome way to let them use such a program and upload the routine to the permanent memory of the chip, preferably without them having to know about coding or use several programs. The light show data is just an array of times paired with codes for changes in the current light state to occur, so essentially I need to load a new array into the chip's flash memory. Whether that can only be done by completely rewriting the chip or not I'm not sure. One option I'm considering is connecting the PIC as an HID device (using a pic 18F4550 or 2550), using an example I've followed before, and a PIC programming language with "persistent" variables which don't clear when the chip shuts down and just having it upload the array to that while connected to the PIC as an HID device. I could probably cobble together some way of doing this, but I'm sure there is a much more professional and sensible way I'm not aware of.
 
Last edited:
You can read and write to the flash memory in 16 word chunks. So just reserve a large area in the ROM and use that.

You don't mention which compiler you will be using but you should be able to reserve an area in the linker script.

Edit, you will have to do something clever with the USB as erasing flash stalls the processor for 2mS and I'm not sure what the USB will do when your chip disappears for 2mS.

Mike.
 
Last edited:
I typically use C18 and MPLAB.

I wasn't aware you could write to the flash memory in chunks. If I decide to go that way I can probably find a tutorial or example on it. How complicated is it, and how complicated to read the data in the pic program?
 
It's fairly straight forward and there are examples in the data sheet. You use the table read/write registers and the hardware does the rest. The only problem I can see is the 2mS dead time whenever a write takes place. However, this must be surmountable as the USB bootloaders do it. That might be a way to go and would be worth looking into.

Mike.
 
Well, they do have example code and a pretty full explanation. I guess I'll just try implementing it and see what it does when the device goes out of communication for a moment. My guess it will either not notice or go "USB COM Device Detected" like it was just plugged in.
 
Last edited:
I found a nice example, posted below. I'm not sure how to use it but I'm sure I can learn if I mess with it enough.

I can see that it declares 2 arrays of addresses and two single variables with addresses. But the use is a bit confusing to me, in this part:

TestVar = ReadEEPROM((unsigned int)&FirstArray[0]);
TestVar = ReadEEPROM((unsigned int)&FirstArray[1]);
TestVar = ReadEEPROM((unsigned int)&FirstArray[2]);

I can see that they are reading one byte (actually it looks like there are 2 bytes at each address?) from each address, when it uses "(unsigned int)&" is that saying to treat it the variable as that type? Also, is this just overwriting TestVar 3 times, or is it somehow appending to it in a way I don't see?

**broken link removed**
Code:
//==================================
// Include files

#include <p18f8720.h>

//==================================
// Function prototypes

unsigned char ReadEEPROM(unsigned int address);
void WriteEEPROM(unsigned int address, unsigned char data);

//==================================
// Declare EEPROM data and other global variables
// Note that EEPROM is mapped to program memory address 0xF00000

#pragma romdata dataEEPROM=0xF00000
rom unsigned char FirstByte = 0x55;
rom unsigned char SecondByte;
rom unsigned char FirstArray[] = {0x00, 0x01, 0x02};
rom unsigned char SecondArray[3];

#pragma udata
unsigned char TestVar;

//==================================
// Main routine 

#pragma code
void main(void)
{
// Configure the device to access Data EEPROM memory
EECON1bits.EEPGD = 0;
EECON1bits.CFGS = 0;

// Read each of the EEPROM bytes defined in FirstByte and FirstArray[]
TestVar = ReadEEPROM((unsigned int)&FirstByte);
TestVar = ReadEEPROM((unsigned int)&FirstArray[0]);
TestVar = ReadEEPROM((unsigned int)&FirstArray[1]);
TestVar = ReadEEPROM((unsigned int)&FirstArray[2]);

// Write to each of the EEPROM locations defined in SecondByte and SecondArray[]
WriteEEPROM((unsigned int)&SecondByte, 0xaa);
WriteEEPROM((unsigned int)&SecondArray[0], 0x07);
WriteEEPROM((unsigned int)&SecondArray[1], 0x08);
WriteEEPROM((unsigned int)&SecondArray[2], 0x09);

// Wait forever
while (1);
}

//==================================
// EEPROM read routine

unsigned char ReadEEPROM(unsigned int Address)
{
EEADRH = (unsigned char)(Address>>8); // Load the high byte of the EEPROM address
EEADR = (unsigned char)Address; // Load the low byte of the EEPROM address
EECON1bits.RD = 1; // Do the read
return EEDATA; // Return with the data
}

//==================================
// EEPROM write routine

void WriteEEPROM(unsigned int Address, unsigned char Data)
{
static unsigned char GIE_Status; // Variable to save Global Interrupt Enable bit

EEADRH = (unsigned char)(Address>>8); // Load the high byte of the EEPROM address
EEADR = (unsigned char)Address; // Load the low byte of the EEPROM address
EEDATA = Data; // Load the EEPROM data
EECON1bits.WREN = 1; // Enable EEPROM writes
GIE_Status = INTCONbits.GIE; // Save the Global Interrupt Enable bit
INTCONbits.GIE = 0; // Disable global interrupts
EECON2 = 0x55; // Required sequence to start the write cycle
EECON2 = 0xAA; // Required sequence to start the write cycle
EECON1bits.WR = 1; // Required sequence to start the write cycle
INTCONbits.GIE = GIE_Status; // Restore the Global Interrupt Enable bit
EECON1bits.WREN = 0; // Disable EEPROM writes
while (EECON1bits.WR); // Wait for the write cycle to complete
}
 
Last edited:
I've modified the code to learn it a little better and it is saving the data and bringing it back.

Code:
//==================================
// Include files

#include <p18f1320.h>

//==================================
// Function prototypes

unsigned char ReadEEPROM(unsigned int address);
void WriteEEPROM(unsigned int address, unsigned char data);

//==================================
// Declare EEPROM data and other global variables
// Note that EEPROM is mapped to program memory address 0xF00000

#pragma romdata dataEEPROM=0xF00000

/*
rom unsigned char FirstByte = 0x55;
rom unsigned char SecondByte;
rom unsigned char FirstArray[] = {0x00, 0x01, 0x02};
rom unsigned char SecondArray[3];
*/

#pragma udata
//unsigned char TestVar;
unsigned char TestVar1;
unsigned char TestVar2;
unsigned char TestVar3;

//==================================
// Main routine 

#pragma code
void main(void)
{
// Configure the device to access Data EEPROM memory
EECON1bits.EEPGD = 0;
EECON1bits.CFGS = 0;

/*
// Read each of the EEPROM bytes defined in FirstByte and FirstArray[]
TestVar = ReadEEPROM((unsigned int)&FirstByte);
TestVar = ReadEEPROM((unsigned int)&FirstArray[0]);
TestVar = ReadEEPROM((unsigned int)&FirstArray[1]);
TestVar = ReadEEPROM((unsigned int)&FirstArray[2]);

// Write to each of the EEPROM locations defined in SecondByte and SecondArray[]
WriteEEPROM((unsigned int)&SecondByte, 0xaa);
WriteEEPROM((unsigned int)&SecondArray[0], 0x07);
WriteEEPROM((unsigned int)&SecondArray[1], 0x08);
WriteEEPROM((unsigned int)&SecondArray[2], 0x09);
*/

TestVar1 = ReadEEPROM(0x00);
TestVar2 = ReadEEPROM(0x01);
TestVar3 = ReadEEPROM(0x02);

WriteEEPROM(0x00, 0x07);
WriteEEPROM(0x01, 0x08);
WriteEEPROM(0x02, 0x09);

// Wait forever
while (1);
}

//==================================
// EEPROM read routine

unsigned char ReadEEPROM(unsigned int Address)
{
//EEADRH = (unsigned char)(Address>>8); // Load the high byte of the EEPROM address
EEADR = (unsigned char)Address; // Load the low byte of the EEPROM address
EECON1bits.RD = 1; // Do the read
return EEDATA; // Return with the data
}

//==================================
// EEPROM write routine

void WriteEEPROM(unsigned int Address, unsigned char Data)
{
static unsigned char GIE_Status; // Variable to save Global Interrupt Enable bit

//EEADRH = (unsigned char)(Address>>8); // Load the high byte of the EEPROM address
EEADR = (unsigned char)Address; // Load the low byte of the EEPROM address
EEDATA = Data; // Load the EEPROM data
EECON1bits.WREN = 1; // Enable EEPROM writes
GIE_Status = INTCONbits.GIE; // Save the Global Interrupt Enable bit
INTCONbits.GIE = 0; // Disable global interrupts
EECON2 = 0x55; // Required sequence to start the write cycle
EECON2 = 0xAA; // Required sequence to start the write cycle
EECON1bits.WR = 1; // Required sequence to start the write cycle
INTCONbits.GIE = GIE_Status; // Restore the Global Interrupt Enable bit
EECON1bits.WREN = 0; // Disable EEPROM writes
while (EECON1bits.WR); // Wait for the write cycle to complete
}

I was having an error with EEADRH so I checked the data sheet and the 18F1320 didn't have a high bit. Does that mean that each address is one byte, or that I need to do something else to access the higher byte? I'm trying to store a lot of data in the 256 bytes I get access to, so I can't waste half of them. Besides blocking that out to get it to compile for the 18F1320 all I did was to create a few variables and address them directly, I also used the same ones so I could run it a few times and see that it had written and loaded the code. It seems to have worked. So now the question is, how to most effectively store data that way?

If anyone is not sure what I'm dealing with code in the PIC for, I already have making the PIC an HID device figured out. So it possible I would have the user plug in the PIC and bulk transfer data into an array, which would then be loaded into the eeprom by the PIC to save it, to avoid wasting space this array would be the same one that the data was stored in for running the light show.

Another thing I was thinking about is how best to balance the use of these bytes. Can I fill the whole 256 it says I have in the data sheet? Or is some of that the program memory? If I can use all 256 I think I could save a light show routine into it, it would just need to be carefully done. I would probably want to let them specify effects to the second, and if one byte of each effect code is time that would leave me with under 5 minutes, on the other hand I don't think I need 256 effect codes:

8 levels of brightness for each of the 3 colors (lowest level turns off, or cancels effect) = 24
Fast strobe on for all 4 sets (blue, green, red, and ultra bright strobes) = 4
slow Pulse for all 3 colors, slow strobe for the ultra brights = 4
the strobe is turned off by turning on any other effect, so strobe off needs no code

So that's 32 codes. Maybe I can narrow it down.

So if I use my byes in pairs I could have 128 effect commands, 10 bits for the timing, giving me up to 1024 seconds = 15 minutes, and that would still leave me 6 bits for 64 possible effect codes. That would be decent.

But maybe I'm counting my chickens before they hatch here because I don't know that I can use all the EEPROM like that. But given that, the question is, how do I make an array of ints (16 bit) store them, and retrieve them, I can use this storage method on bytes but I'm not sure how to break up the data and then put it back together.
 
Last edited:
The code you are using is for the data EEPROM. The FLASH memory is completely different.

Here is example code that defines an area at 0x2000 that is 0x1000 long. It erases it (to 0xff) and then writes "Hello World!" to all the area. Have a play with it and see how you go.
Code:
#pragma config WDT=OFF, LVP=OFF, DEBUG=ON, MCLRE = ON 
#pragma config FOSC = INTOSCIO_EC

#include <p18f4550.h>
 

#pragma romdata myDataSection=0x2000
rom unsigned char myData[0x1000];
#pragma udata

void ClrData(void){
int i;
    for(i=0;i<sizeof(myData);i+=64){
        myData[i]=0xff;
        EECON1bits.EEPGD=1;
        EECON1bits.CFGS=0;
        EECON1bits.WREN=1;
        EECON1bits.FREE=1;
        EECON2=0x55;
        EECON2=0xaa;
        EECON1bits.WR=1;
    }
}

void WriteData(unsigned int Add,unsigned char Data){
    myData[Add]=Data;
    if((Add & 0x1f) == 0x1f){
        EECON1bits.EEPGD=1;
        EECON1bits.CFGS=0;
        EECON1bits.WREN=1;
        EECON2=0x55;
        EECON2=0xaa;
        EECON1bits.WR=1;
    }    
}

void ForceWrite(void){
    EECON1bits.EEPGD=1;
    EECON1bits.CFGS=0;
    EECON1bits.WREN=1;
    EECON2=0x55;
    EECON2=0xaa;
    EECON1bits.WR=1;
}

void main()
{
unsigned int i;
unsigned char j, String[]="Hello World! ";
unsigned int Example=0x1234;
	while(!OSCCONbits.IOFS);      //wait for osc stable
    ClrData();
    j=0;
    for(i=2;i<sizeof(myData);i++){
        WriteData(i,String[j++]);
        if(String[j]==0)
            j=0;
    }
    //to write an integer do,
    WriteData(0,Example&255);   //write low byte
    WriteData(1,Example>>8);    //write high byte
    ForceWrite();
	while(1);
}

Note that the memory is erased 64 bytes at a time and written 32 bytes at a time. The memory only gets written after 32 bytes are sent to WriteData().

To write an integer you would write it as low byte and high byte.
The ForceWrite routine is for if you data doesn't end on a 32 byte boundary.

Mike.
P.S. Sorry it's been so long since I checked the board, girlfriend gets priority at weekends.
 
Last edited:
Have you managed to try this yet? I'm curious to know what happens to the USB during the 2mS pause. Does it re-enumerate or does it just carry on as normal?

Mike.
 
Friday-Wednesday are my busy days. I'm working on trying it out now. It looks like it will do the trick, I need more practice with pulling bits apart into the groups I want.

I literally just got back to this so I might have some results, or at least more questions soon. So, this being flash memory, do I have a bit more memory to work with? Thanks for the example code, it looks very useful.
 
So, this being flash memory, do I have a bit more memory to work with?

The 18F4550 has 32k (32768 bytes or 16384 words) of flash memory which is more than most people can ever use for code.

Mike.
 
Cool, considering I was looking at fitting my whole routine onto 256 bytes I think even a tiny fraction of that will be plenty.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top