I’d like to have a string stored even when power is removed, and thought the CC2511 flash memory would do this. The data sheet says most XDATA locations are in flash, but is there any way to force a variable to be placed in there? I’ve done a quick test and after a reset the contents of the memory are gone. I declared it as
char XDATA description[32];
Or should I be using an external EEPROM chip? I only want to store about 128 bytes of info, at most, so a separate chip seems silly.
I am not sure what sentence you found in the datasheet, but I think you are misinterpreting it. If you declare a variable using “XDATA”, it will be stored in the “external RAM”, not flash. I think you can easily put a string in flash by doing something like this:
CODE char mystring[] = "hello world";
(If that doesn’t work, try replacing CODE with “const” or “const CODE”.)
If you want to have a string that your program can change on the fly, it will be harder. Unfortunately, we do not have a nice library for doing it, but you can read, erase, and write blocks from the flash on the CC2511. I recommend reading the CC2511 datasheet for information about how to do that, and feel free to ask here when you get stuck.
I do use the CODE qualifier for my constants, but as you guessed I want to be able to write to this storage.
Looking at figure 16 on page 42 of the datasheet, am I correct in saying the flash memory is from addresses 0x0000 - 0x7fff? If so:
The boot loader is from 0x0000 - 0x03FF, and 0x7800 - 0x7FFF (1st and last two pages of flash).
Applications are written starting at 0x0400, and all CODE stuff is in a contiguous block starting from there.
I can use flash pages from the 3rd last, counting downwards to store my info as long as my CODE doesn’t take all 29kb.
My interpretation of the datasheet is that I have to first erase a whole 1kb page if I want to write to flash. So I’d erase say the 3rd last block and then set up a DMA transfer to put the info info there. Then I can read the info out by setting a pointer to the beginning of the block:
uint8* ptr = 0x7400; //to read from the 3rd last block of flash.
If all that is correct the only thing I don’t know how to do is to ensure the condition in 12.3.2.1 pp83 that the instruction to trigger the DMA is aligned on a two byte boundary. Is there a directive I can put in C code to force this?
Everything you said up to the last paragraph is true. As far as I know, SDCC does not have a good way of aligning code. That would be very useful, not only here but also in making well-defined delay loops. However, section 12.3.2.1 of the datasheet says:
The bold in that quote was added by me. So the solution is to put a little bit of code into RAM and run that when you need to trigger the DMA. I don’t have a great way of putting functions into RAM, but you just need one tiny function so I “assembled” it by hand:
XDATA uint8 startFlashWrite[] = {
0x75, 0xAE, 0x02, // mov _FCTL, #2 : Sets FCTRL.WRITE bit to 1, initiating a write to flash.
0x22 // ret : Returns to the calling function.
};
I did understand the caveat about the alignment being required only for code in FLASH, but I didn’t know how to ensure code was in RAM. It didn’t occur to me to just put the bytes of the funciton into a byte array. Something I don’t get, and didn’t understand in the datasheet either, is how the alignment is ensured. Are vars just alinged on 2-byte boundaries? I don’t see any alignment directives in your code or the datasheet asm.
I’m enjoying using the wixel, it’s a very interesting little device, and the SDK libs make it really easy to program for. It wouldn’t be quite so much fun if we had to program the bare metal, unless getting paid for it of course
Oh, that .even thing is nice, if that works. I think I must have tried something like that before and it didn’t work. It might only ensure the alignment within the object file, but the object might get loaded into flash starting at an odd address. You should look at the .rst file output by the compiler to see if it really works.
The program doesn’t crash, but my pattern of 01 02 03… doesn’t make it into flash. Given it doesn’t work with either triggering function I’m assuming my DMA channel setup is incorrect. Pointer manipulation isn’t my strong point any more. The string to the USB port is sent during the same run, so the flash hasn’t been erased by a program load or anything.
SDCC’s generic pointers should be avoided when possible because you can already point to almost anything using a pointer to XDATA.
Your lines that set DESTADDRH and DESTADDRL are missing the & operator so they would not actually take an address, but read a value from FWDATA. However, it’s not so simple to get an XDATA address of an SFR, so you should do this instead:
Section 12.3.5 tells us to set FWT to a value that depending on the clock frequency. For the Wixel it should be:
FWT = 32;
It would make me comfortable if you use MOV instead of ORL on FCTL, because then you have a better guarantee about what the value of FCTL will be, but that shouldn’t be a problem at this point.
Also, please note that since you are using _2, your code might stop working when we assign DMA channel 2 to some other purpose in the future.
That wasn’t an accident, it was a misunderstanding. I don’t understand the 8051 well enough to know there are different pointer types. I thought the XDATA directive was used to say where to store the variable being declared, not that it specifies what type of memory is being pointed to.
Thanks, I’d never have figured that out.
Thanks.
Is there a more generic form of code which will use a free DMA channel, or should I use channel 4 because Pololu will use them from 1 up, or is this just a warning that my code might need to change in future?
It’s just a warning that your code might need to change in the future. The next DMA channel we assign to something will probably be 2, so your code would be safer if you used _4 or if you set yourself up to use DMA channel 0. However, I recommend getting your existing code to work before you worry about that.
I used the function in FLASH with the .even directive to trigger it. Still not sure if the directive works as looking at the listing I think the instruction naturally ended up on an even boundary. I’ll worry about that later.
Thanks for your help!
I’ll change over to DMA 4 and later implement some round-robin set of pages to write to so the FLASH doesn’t die too quickly.
I’m new to wixel programming, and it’s my first time programming in C
I face the same problem, e.g. I need variables store permanently that I can change within my code. Would it be possible to post the complete working code to do that? I find it very hard to get working code out of this conversation. Sorry to be a numpty.
I recommend reading section 12.3 of the CC2511F32 datasheet, which describes how to write to flash memory. Also, there is another thread that has some more complete code examples that you might find useful: