I'm using a STM32F401VCT6U "discovery" board, and I need to provide a way for the user to write addresses in memory at runtime.
I wrote what can be simplified to the following function:
uint8_t Write(uint32_t address, uint8_t* values, uint8_t count)
{
uint8_t index;
for (index = 0; index < count; ++index) {
if (IS_FLASH_ADDRESS(address+index)) {
/* flash write */
FLASH_Unlock();
if (FLASH_ProgramByte(address+index, values[index]) != FLASH_COMPLETE) {
return FLASH_ERROR;
}
FLASH_Lock();
} else {
/* ram write */
((uint8_t*)address)[index] = values[index]
}
}
return NO_ERROR;
}
In the above, address is the base address, values is a buffer of size at least count which contains the bytes to write to memory and count the number of bytes to write.
Now, my problem is the following: when the above function is called with a base address in flash and count=100, it works normally the first few times, writing the passed values buffer to flash. After those first few calls however, I cannot write just any value anymore: I can only reset bits in the values in flash, eg an attempt to write 0xFF to 0x7F will leave 0x7F in the flash, while writing 0xFE to 0x7F will leave 0x7E, and 0x00 to any value will be successful (but no other value will be writable to the address afterwards).
I can still write normally to other addresses in the flash by changing the base address, but again only a few times (two or three calls with count=100).
This behaviour suggests that the maximum write count of the flash has been reached, but I cannot imagine it can be so fast. I'd expect at the very least 10,000 writes before exhaustion.
So what am I doing wrong?
You have missunderstood how flash works - it is not for example as straight forward as writing EEPROM. The behaviour you are discribing is normal for flash.
To repeatidly write the same address of flash the whole sector must be first erased using FLASH_EraseSector. Generally any data that needs to preserved during this erase needs to be either buffered in RAM or in another flash sector.
If you are repeatidly writing a small block of data and are worried about flash burnout do to many erase write cycles you would want to write an interface to the flash where each write you move your data along the flash sector to unwriten flash, keeping track of its current offset from the start of sector. Only then when you run out of bytes in the sector would you need to erase and start again at start of sector.
ST's "right way" is detailed in AN3969: EEPROM emulation in STM32F40x/STM32F41x microcontrollers
This is more or less the process:
Reserve two Flash pages
Write the latest data to the next available location along with its 'EEPROM address'
When you run out of room on the first page, write all of the latest values to the second page and erase the first
Begin writing values where you left off on page 2
When you run out of room on page 2, repeat on page 1
This is insane, but I didn't come up with it.
I have a working and tested solution, but it is rather different from #Ricibob's answer, so I decided to make this an answer.
Since my user can write anywhere in select flash sector, my application cannot handle the responsability of erasing the sector when needed while buffering to RAM only the data that need to be preserved.
As a result, I transferred to my user the responsability of erasing the sector when a write to it doesn't work (this way, the user remains free to use another address in the sector to avoid too many write-erase cycles).
Solution
Basically, I expose a write(uint32_t startAddress, uint8_t count, uint8_t* values) function that has a WRITE_SUCCESSFUL return code and a CANNOT_WRITE_FLASH in case of failure.
I also provide my user with a getSector(uint32_t address) function that returns the id, start address and end address of the sector corresponding to the address passed as a parameter. This way, the user knows what range of address is affected by the erase operation.
Lastly, I expose an eraseSector(uint8_t sectorID) function that erase the flash sector whose id has been passed as a parameter.
Erase Policy
The policy for a failed write is different from #Ricibob's suggestion of "erase if the value in flash is different of FF", as it is documented in the Flash programming manual that a write will succeed as long as it is only bitreset (which matches the behavior I observed in the question):
Note: Successive write operations are possible without the need of an erase operation when
changing bits from ‘1’ to ‘0’.
Writing ‘1’ requires a Flash memory erase operation.
If an erase and a program operation are requested simultaneously, the erase operation is
performed first.
So I use the macro CAN_WRITE(a,b), where a is the original value in flash and b the desired value. The macro is defined as:
!(~a & b)
which works because:
the logical not (!) will transform 0 to true and everything else to false, so ~a & b must equal 0 for the macro to be true;
any bit at 1 in a is at 0 in ~a, so it will be 0 whatever its value in b is (you can transform a 1 in 1 or 0);
if a bit is 0 in a, then it is 1 in ~a, if b equals 1 then ~a & b != 0 and we cannot write, if bequals 0 it's OK (you can transform a 0 to 0 only, not to 1).
List of flash sector in STM32F4
Lastly and for future reference (as it is not that easy to find), the list of sectors of flash in STM32 can be found on page 7 of the Flash programming manual.
Related
I will start a project which needs a QSPI protocol. The component I will use is a 16-bit ADC which supports QSPI with all combinations of clock phase and polarity. Unfortunately, I couldn't find a source on the internet that points to QSPI on STM32, which works with other components rather than Flash memories. Now, my question: Can I use STM32's QSPI protocol to communicate with other devices that support QSPI? Or is it just configured to be used for memories?
The ADC component I want to use is: ADS9224R (16-bit, 3MSPS)
Here is the image of the datasheet that illustrates this device supports the full QSPI protocol.
Many thanks
page 33 of the datasheet
The STM32 QSPI can work in several modes. The Memory Mapped mode is specifically designed for memories. The Indirect mode however can be used for any peripheral. In this mode you can specify the format of the commands that are exchanged: presence of an instruction, of an adress, of data, etc...
See register QUADSPI_CCR.
QUADSPI supports indirect mode, where for each data transaction you manually specify command, number of bytes in address part, number of data bytes, number of lines used for each part of the communication and so on. Don't know whether HAL supports all of that, it would probably be more efficient to work directly with QUADSPI registers - there are simply too many levers and controls you need to set up, and if the library is missing something, things may not work as you want, and QUADSPI is pretty unpleasant to debug. Luckily, after initial setup, you probably won't need to change very much in its settings.
In fact, some time ago, when I was learning QUADSPI, I wrote my own indirect read/write for QUADSPI flash. Purely a demo program for myself. With a bit of tweaking it shouldn't be hard to adapt it. From my personal experience, QUADSPI is a little hard at first, I spent a pair of weeks debugging it with logic analyzer until I got it to work. Or maybe it was due to my general inexperience.
Below you can find one of my functions, which can be used after initial setup of QUADSPI. Other communication functions are around the same length. You only need to set some settings in a few registers. Be careful with the order of your register manipulations - there is no "start communication" flag/bit/command. Communication starts automatically when you set some parameters in specific registers. This is explicitly stated in the reference manual, QUADSPI section, which was the only documentation I used to write my code. There is surprisingly limited information on QUADSPI available on the Internet, even less with registers.
Here is a piece from my basic example code on registers:
void QSPI_readMemoryBytesQuad(uint32_t address, uint32_t length, uint8_t destination[]) {
while (QUADSPI->SR & QUADSPI_SR_BUSY); //Make sure no operation is going on
QUADSPI->FCR = QUADSPI_FCR_CTOF | QUADSPI_FCR_CSMF | QUADSPI_FCR_CTCF | QUADSPI_FCR_CTEF; // clear all flags
QUADSPI->DLR = length - 1U; //Set number of bytes to read
QUADSPI->CR = (QUADSPI->CR & ~(QUADSPI_CR_FTHRES)) | (0x00 << QUADSPI_CR_FTHRES_Pos); //Set FIFO threshold to 1
/*
* Set communication configuration register
* Functional mode: Indirect read
* Data mode: 4 Lines
* Instruction mode: 4 Lines
* Address mode: 4 Lines
* Address size: 24 Bits
* Dummy cycles: 6 Cycles
* Instruction: Quad Output Fast Read
*
* Set 24-bit Address
*
*/
QUADSPI->CCR =
(QSPI_FMODE_INDIRECT_READ << QUADSPI_CCR_FMODE_Pos) |
(QIO_QUAD << QUADSPI_CCR_DMODE_Pos) |
(QIO_QUAD << QUADSPI_CCR_IMODE_Pos) |
(QIO_QUAD << QUADSPI_CCR_ADMODE_Pos) |
(QSPI_ADSIZE_24 << QUADSPI_CCR_ADSIZE_Pos) |
(0x06 << QUADSPI_CCR_DCYC_Pos) |
(MT25QL128ABA1EW9_COMMAND_QUAD_OUTPUT_FAST_READ << QUADSPI_CCR_INSTRUCTION_Pos);
QUADSPI->AR = (0xFFFFFF) & address;
/* ---------- Communication Starts Automatically ----------*/
while (QUADSPI->SR & QUADSPI_SR_BUSY) {
if (QUADSPI->SR & QUADSPI_SR_FTF) {
*destination = *((uint8_t*) &(QUADSPI->DR)); //Read a byte from data register, byte access
destination++;
}
}
QUADSPI->FCR = QUADSPI_FCR_CTOF | QUADSPI_FCR_CSMF | QUADSPI_FCR_CTCF | QUADSPI_FCR_CTEF; //Clear flags
}
It is a little crude, but it may be a good starting point for you, and it's well-tested and definitely works. You can find all my functions here (GitHub). Combine it with reading the QUADSPI section of the reference manual, and you should start to get a grasp of how to make it work.
Your job will be to determine what kind of commands and in what format you need to send to your QSPI slave device. That information is available in the device's datasheet. Make sure you send command and address and every other part on the correct number of QUADSPI lines. For example, sometimes you need to have command on 1 line and data on all 4, all in the same transaction. Make sure you set dummy cycles, if they are required for some operation. Pay special attention at how you read data that you receive via QUADSPI. You can read it in 32-bit words at once (if incoming data is a whole number of 32-bit words). In my case - in the function provided here - I read it by individual bytes, hence such a scary looking *destination = *((uint8_t*) &(QUADSPI->DR));, where I take an address of the data register, cast it to pointer to uint8_t and dereference it. Otherwise, if you read DR just as QUADSPI->DR, your MCU reads 32-bit word for every byte that arrives, and QUADSPI goes crazy and hangs and shows various errors and triggers FIFO threshold flags and stuff. Just be mindful of how you read that register.
Computer System Architecture - Morris Mano In chapter 5 section 7 figure 5-13
When IEN it checks whether "FGI" or "FGO" are set to 1 then an interrupt cycle happens, but as I know is when FGI = 1 it means that information in INPR cannot be changed, and FGO is the reverse to that which means that when FGO is set to 1 then information in AC will be transferred to OUTR 'OUTR can be changed' so the question here shouldn't the condition of applying interrupt cycle happen when "FGI" = 0 or "FGO" = 1 since INPR or OUTR can be changed under these conditions which now make since to execute an interrupt?
Either flag being 1 logically means a device is "ready", but what "ready" means differs for input and for output devices. In either case, flag being 1 means that the processor can or should now take action.
FGI=1 means the input device is ready, but that really means a new input is available (e.g. the user typed a key on a keyboard) and the processor should accept it. FGO=1 prevents the input device(s) from overwriting a prior input held in INPR that the processor hasn't accepted yet. When the processor accepts the input, FGI goes to 0 unlocking the INPR register, and that allows the input device to write again, which it will eventually do when the user presses another key (sending FGI back to 1 to signal the processor).
FGO=1 means ready for output, which really means the last output has been fully accepted by the device, so the OUTR register is unlocked for the processor to write a new data (character for the console). FGO=0 prevents the processor from writing OUTR as the output device hasn't accepted the last one yet.
The interrupt service routine should check each flag, and if FGI=1 then accept an input (INPR->AC) and move it into a buffer for the user program to read when it is ready. Whereas if FGO=1, then move an output character from memory buffer into the AC, and then do AC->OUTR, also lowering FGO to 0, which will preclude the processor writing until that data has been accepted by the device.
So, FGI=0 means that the processor has accepted the prior INPR value provided by the input device, and there is no new character as yet but the register is unlocked so the device can write at will.
FGO=0 means that the processor has written a value to the OUTR register, but the output device hasn't accepted that yet, so the register should be considered locked.
I'm working on project on STM32L152RCT6, where i have to build a mechanism to self update the code from the newly gated file(HEX file).
For that i have implemented such mechanism like boot loader where it checks for the new firmware if there it it has to cross verify and if found valid it has to store on "Application location".
I'm taking following steps.
Boot loader address = 0x08000000
Application address = 0x08008000
Somewhere on specified location it has to check for new file through Boot loader program.
If found valid it has to be copy all the HEX on location(as per the guide).
Than running the application code through jump on that location.
Now problem comes from step 5, all the above steps I've done even storing of data has been done properly(verify in STM32 utility), but when i'm jump to the application code it won't work.
Is there i have to cross check or something i'm missing?
Unlike other ARM controllers that directly jump to address 0 at reset, the Cortex-M series takes the start address from a vector table. If the program is loaded directly (without a bootloader), the vector table is at the start of the binary (loaded or mapped to address 0). First entry at offset 0 is the initial value of the stack pointer, second entry at address 4 is called the reset vector, it contains the address of the first instruction to be executed.
Programs loaded with a bootloader usually preserve this arrangement, and put the vector table at the start of the binary, 0x08008000 in your case. Then the reset vector would be at 0x08008004. But it's your application, you should check where did you put your vector table. Hint: look at the .map file generated by the linker to be sure. If it's indeed at 0x08008000, then you can transfer control to the application reset vector so:
void (*app)(void); // declare a pointer to a function
app = *(void (**)(void))0x08008004; // see below
app(); // invoke the function through the pointer
The complicated cast in the second line converts the physical address to a pointer to a pointer to a function, takes the value pointed to it, which is now a pointer to a function, and assigns it to app.
Then you should manage the switchover to the application vector table. You can do it either in the bootloader or in the application, or divide the steps between them.
Disable all interrupts and stop SysTick. Note that SysTick is not an interrupt, don't call NVIC_DisableIRQ() on it. I'd do this step in the bootloader, so it gets responsible to disable whatever it has enabled.
Assign the new vector table address to SCB->VTOR. Beware that the boilerplate SystemInit() function in system_stm32l1xx.c unconditionally changes SCB->VTOR back to the start of the flash, i.e. to 0x08000000, you should edit it to use the proper offset.
You can load the stack pointer value from the vector table too, but it's tricky to do it properly, and not really necessary, the application can just continue to use the stack that was set up in the bootloader. Just check it to make sure it's reasonable.
Have you changed the application according to the new falsh position?
For example the Vector Table has to be set correctl via
SCB->VTOR = ...
When your bootloader starts the app it has to configure everything back to the reset state as the application may relay on the default reset values. Espessially you need to:
Return values of all hardware registers to its reset values
Switch off all peripheral clocks (do not forget about the SysTick)
Disable all enabled interrupts
Return all clock domains to its reset values.
Set the vector table address
Load the stack pointer from the beginning of the APP vector table.
Call the APP entry point.(vertor table start + 4)
Your app has to be compiled and linked using the custom linker script where the FLASH start point is 0x8008000
for example:
FLASH (rx) : ORIGIN = 0x8000000 + 32K, LENGTH = 512K - 32K
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
where FLASH_BASE's value must be equal to the address of your IROM's value in KEIL
example:
#define FLASH_BASE 0x08004000
Keil configuration
I'm trying to read from my XBox 360 controller without polling it. (To be precise, I'm actually using a Logitech F310, but my Windows 10 PC sees it as an XBox 360 controller.) I've written some rather nasty HID code that uses overlapping I/O to block in a thread on two events, one that indicates there is a report ready to read from the HID device, the other indicating the UI thread has requested the HID thread to exit. That works fine, but the HID driver behaves somewhat differently than XInput does. In particular, it consolidates the two triggers into a single value, only passing their difference (on the curious claim that games expect HID values to be 0x80 when the player's finger is off the control). XInput treats them as two distinct values, which is a big improvement. Also, XInput reports the hat switches as four bits, which means you can actually get ten states out of it: unpressed, N, NE, E, SE, S, SW, W, NW, and all-down (that last might be hard to use successfully, but at least it's there if you want it; I've been using it to exit my polling loop).
The downside, to me, of XInput is that there appears to be no way to block on a read request until the controller changes one of its values or buttons. As an HID device, the ReadFile call will block (more exactly, WaitForMultipleEvents blocks until there is data available). XInput seems to anticipate polling. For a game that would naturally be written to poll the controller as often as it updated the game state (maybe once for each new video frame displayed, for example), that makes sense. But if you want to use the controller for some other purpose (I'm working on a theatrical application), you might want a purely asynchronous system like the HID API supplies. But, again, the HID API combines the two value triggers.
Now, when you read the device with XInput, not only do you get the state of all the controls, you also get a packet number. MSDN says the packet number only changes when the state of a control changes. That way, if consecutive packet numbers are the same, you don't have to bother with any processing after the first one, because you know the controller state hasn't changed. But you are still polling which, to me, is somewhat vulgar.
What intrigues me, however, is that when I put a big delay in between my polls (100ms) I can see that the packet numbers go up by more than one when the value controls (the triggers or sticks) are being moved. This, I think, suggests that the device is sending packets without waiting to be polled, and that I am only getting the most recent packet each time I poll. If that is the case, it seems that I ought to be able to block until a packet is sent, and react only when that happens, rather than having to poll at all. But I can't find any indication that this is an option. Because I can block with the HID API, I don't want to give up without trying (including asking for advice here).
Short of writing my own driver for the controller (which I'm not sure is even an option without proprietary documentation), does anyone know how I can use overlapping I/O (or any other blocking method) to read the XBox 360 controller the way XInput does, with the triggers as separate values, and the hat as four buttons?
Below is some code I wrote that reads the controller and shows that the packet numbers can jump by more than one between reads:
#include <Windows.h>
#include <Xinput.h>
#include <stdio.h>
#define MAX_CONTROLLERS 4
int main()
{
DWORD userIndex;
XINPUT_STATE xs;
XINPUT_VIBRATION v;
XInputEnable(TRUE);
// Which one are we?
for (userIndex = 0; userIndex < XUSER_MAX_COUNT; ++userIndex)
if (XInputGetState(userIndex, &xs) == ERROR_SUCCESS)
break;
if (userIndex == XUSER_MAX_COUNT)
{
printf("Couldn't find an Xbox 360 controller.\n");
getchar();
return -1;
}
printf("Using controller #%1d.\n", userIndex);
while (TRUE)
{
DWORD res = XInputGetState(userIndex, &xs);
printf("%5d %6d: %3d %3d %3d %3d %3d %3d 0x%04X\n",
res,
xs.dwPacketNumber,
xs.Gamepad.bLeftTrigger & 0xFF,
xs.Gamepad.bRightTrigger & 0xFF,
xs.Gamepad.sThumbLX & 0xFF,
xs.Gamepad.sThumbLY & 0xFF,
xs.Gamepad.sThumbRX & 0xFF,
xs.Gamepad.sThumbRY & 0xFF,
xs.Gamepad.wButtons);
if (xs.Gamepad.wButtons == 0x000F) // mash down the hat
break;
Sleep(100);
}
getchar();
return 0;
}
Please note that DirectInput isn't much help, as it also combines the triggers into one value.
Thanks!
Not sure there is any advantage to this, but could you write a thread that polls on a regular interval and then sets a semaphore (or some other signal) when the state has changed. Then your main thread could block waiting for the signal from the polling thread. But potentially there might not be any advantage to this system because on some controllers the values of the thumbsticks change slightly ever frame whether you move them or not. (Noise) You could of course ignore small changes and only signal your semaphore when a large change occurred.
I got this error on a CJ1W-CT021 card. It happen all of a sudden after its been running the program for some time. How i found it was by going to the IO Table and Unit Set up. Clicked on parameters for that card and found two settings in red.
Output Control Mode and And/Or Counter Output Patterns. This was there reading
Output Control Mode = 0x40 No Applicable Set Data
And/Or Counter Output Patterns = 0x64 No Applicable Set Data
no idea on how or why these would change they should of been
Output Control Mode = Range Mode
And/Or Counter Output Patterns = Logically Or
I have added some new code, but nothing big or really even used as i had the outputs of the new rungs jumped out. One thing i thought might cause this is every cycle of the program it was checking the value of an encoder connected to this card. Maybe checking it too offten? Anyhow if anyone has any idea what these do or how they would change please post.
Thanks
Glen
EDIT.. I wanted to add the bits i used, dont think any are part of this cards internal io but i may be wrong?
Work bits 66.01 - 66.06 , 60.02 - 60.07 , 160.12, 160.01 - 160.04, 161.02, 161.03
and
Data Bits (D)20720, 20500, 20600, 20000, 20590, 20040
I would check section 4-1 through 4-2-4 of the CT021 manual - make sure you aren't writing to reserved memory locations used for configuration data of the CT021 unit.
EDIT:
1) Check Page 26 of the above manual to see the location of the machine switch settings. The bottom dial sets the '1's digit and the top dial sets the '10's digit (ie machine number can be 0-99);
2) Per page 94, D-Memory is allocated from D20000 + (N X 100) (400 Words) where N is equal to the machine number.
I would guess that your machine number is set to 0 (ie: both dials at '0'), 5, or 6. In the case of machine number '0', this would make the reserved DM range D20000 -> D20399. In this case (see pages 97, 105) D20000 would contain configuration data for Output Control Mode (bits 00-07) and Counter Output Patterns (bits 08-15). It looks like you are writing 0x6440 to D20000 (or D20500, D20600 for machine number 5 or 6, respectively) and are corrupting the configuration data.
If your machine number is 0 then stay away from D20000-D20399 unless you are directly trying to modify the counter's configuration state (ie: don't use them in your program!).
If the machine number is 1 then likewise for D20100-D20499, etc. If you have multiple counters they can overlap ranges so they should always be set with machine numbers which are 4 apart from each other.