uint32_t PAGEError = 0;
FLASH_EraseInitTypeDef EraseInitStruct;
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS ;
EraseInitStruct.Sector = FLASH_SECTOR_0;
EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x08000000, counter)
HAL_FLASH_Lock();
counter2 = *(__IO uint32_t *)0x08000000;
counter3 = *(__IO uint32_t *)0x08000001;
counter4 = *(__IO uint32_t *)0x08000002;
sprintf(buf, "%d", counter2); //gets send to the OLED with I2C
sprintf(buf2, "%d", counter3);
sprintf(buf3, "%d", counter4);
I want to write the variable counter to the flash and then read it as counter2.
The first flash sector starts at 0x08000000.
counter2, 3 and 4 are display trough an OLED screen.
Displaying counter2 works and shows me the value of counter-1, but it works only once. If I write again to the flash nothing seems to happen.
counter3 and counter4 don't work at all.
Output on the OLED when counter=0x00000008 after I have erased the flash but not written anything:
counter2: 536873624
counter3: -652214262
counter4: 31006720
And after writting and ressetting:
counter2: 8
counter3: -654311424
counter4: 30998528
What is going on here? Can someone tell me why all variables change?
Do I have to configure the linker?
I will treat you now as begineer, but will say sorry if you are not.
STM32 devices have flash on 0x08000000 and by erasing this sector, you did failure on startup because you erased actual part from where CPU loads instructions.
When you tried to erase sectors, you did not specify how many sectors to erase.
Reading of counters is wrong. Since you have uint32_t variable, you have to do 4-bytes between readings, something like:
counter2 = *(__IO uint32_t *)0x08000000;
counter3 = *(__IO uint32_t *)0x08000004;
counter4 = *(__IO uint32_t *)0x08000008;
Correct erasing is shown below.
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
EraseInitStruct.Sector = FLASH_SECTOR_0; //Specify sector number
EraseInitStruct.NbSectors = 1; //This is also important!
if(HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {
//Erase error!
}
So, find out how long is your program and do your operations in sector after your program.
You can find example for EraseProgram in STM32CubeF4 package.
STM32Cube_FW_F4_V1.16.0\Projects\STM324x9I_EVAL\Examples\FLASH\FLASH_EraseProgram\Src\main.c
Concept will work also on your nucleo, just make sure you set correct address for flash erasing.
Thanks to #phoenix!
In Stm32CubF3 reference erasing flash works as follows:
There is to mention how a memory page address is looking:
/* Base address of the Flash sectors */
#define ADDR_FLASH_PAGE_0 ((uint32_t)0x08000000) /* Base # of Page 0, 2 Kbytes */
#define ADDR_FLASH_PAGE_1 ((uint32_t)0x08000800) /* Base # of Page 1, 2 Kbytes */
#define ADDR_FLASH_PAGE_2 ((uint32_t)0x08001000) /* Base # of Page 2, 2 Kbytes */
#define ADDR_FLASH_PAGE_3 ((uint32_t)0x08001800) /* Base # of Page 3, 2 Kbytes */
#define ADDR_FLASH_PAGE_4 ((uint32_t)0x08002000) /* Base # of Page 4, 2 Kbytes */
#define ADDR_FLASH_PAGE_5 ((uint32_t)0x08002800) /* Base # of Page 5, 2 Kbytes */
#define ADDR_FLASH_PAGE_6 ((uint32_t)0x08003000) /* Base # of Page 6, 2 Kbytes */
So your minimal code looks as follows:
I want to delete my application # 0x08003000 --> FLASH_PAGE_6. The erasing is done 2kB wise:
/* EEPROM start address in Flash */
#define EEPROM_START_ADDRESS ((uint32_t)ADDR_FLASH_PAGE_32) /* EEPROM emulation start address */
/* Pages 0 and 1 base and end addresses */
#define PAGE0_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x0000))
#define PAGE0_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (PAGE_SIZE - 1)))
//#define PAGE0_ID ADDR_FLASH_PAGE_32
/* Clear flash flags */
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP |
FLASH_FLAG_WRPERR |
FLASH_FLAG_PGERR);
uint32_t *flash_ptr = 0x8003000;
uint32_t page_error = 0;
HAL_StatusTypeDef flashstatus;
FLASH_EraseInitTypeDef s_eraseinit;
/* Fill EraseInit structure*/
s_eraseinit.TypeErase = FLASH_TYPEERASE_PAGES;
s_eraseinit.NbPages = 1; // seems to be 1 !!!!
/* I want to delete 54kB in my flash --> 52kB / 2kB (per erase) = 26 iterations
After erasing one page, increment page by 0x800 = 2048 byte = 2kB = pagesize */
for(int pageCount = 0; pageCount < 26; pageCount++){
s_eraseinit.PageAddress = ADDR_FLASH_PAGE_6 + pageCount * 0x800;
flashstatus = HAL_FLASHEx_Erase(&s_eraseinit, &page_error);
}
The best way is to create the new segment of flash in the linkescriptr and place the data there. It is the safest.
If do not know linker scripts create a table of the one segment size, and place it at the end of the flash using your compiler directives.
If you do not know both I suggest a ready made STM eeprom emulation example from the Cube
Related
I am working on an embedded C project in Keil µVision. Part of the project is to make the uC detect and operate as a USB flash drive. I am using a STM32L072 that is connected to external SPI Flash via SPI2 (P/N: SST26VF016B). I can read/write/erase/ the SST26V flash chip with no issues and I can communicate over USB with no problems. If I plug the device into a Windows based computer via USB it detects as a flash drive with the appropriate capacity (in this case 64KB[see NOTE 1 below]). Windows then will prompt me to format the drive. When I try to format the drive with the following parameters:
(Capacity: 64KB, File System: FAT (Default), Allocation unit size: 512 bytes, Quick Format: checked)
Windows will issue a bunch of reads and writes and eventually fail with an error messages that reads "Windows was unable to complete the format".
I seem to be having a problem bridging the gap between USB and SPI. I modified the usbd_storage_if.c following tutorials like this one. I only modified a few lines of code shown below:
#define STORAGE_LUN_NBR 1 // Logical Unit Number
//#define STORAGE_BLK_NBR 3840 // Use all memory in the SST26V
#define STORAGE_BLK_NBR 80 // Use ONLY the first 64kB of flash for testing.
#define STORAGE_BLK_SIZ 0x200
#define SST26V_MEMORY_OFFSET 0x10000 // This is the starting address of the SST26V's memory
....
/**
* #brief .
* #param lun: .
* #retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 6 */
printf("\r\nR%d:0x%08X", blk_len*STORAGE_BLK_SIZ, (blk_addr * STORAGE_BLK_SIZ + SST26V_MEMORY_OFFSET)); // print the size and address
_SPI_FLASH_GetData((blk_addr * STORAGE_BLK_SIZ + SST26V_MEMORY_OFFSET), blk_len*STORAGE_BLK_SIZ, buf);
return (USBD_OK);
/* USER CODE END 6 */
}
/**
* #brief .
* #param lun: .
* #retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 7 */
// incoming USB data packets are 512Bytes
printf("\r\nW%d:0x%08X", blk_len*STORAGE_BLK_SIZ, (blk_addr * STORAGE_BLK_SIZ + SST26V_MEMORY_OFFSET)); // print the size and address
_SPI_FLASH_WritePage(256, (blk_addr * STORAGE_BLK_SIZ + SST26V_MEMORY_OFFSET), buf); // As per SST26V datasheet, can only write 256B at a time
_SPI_FLASH_WritePage(256, (blk_addr * STORAGE_BLK_SIZ + SST26V_MEMORY_OFFSET + 256), buf+256);
return (USBD_OK);
/* USER CODE END 7 */
}
If I attempt to format the drive and then dump the 64KB flash contents over UART I get the following:
:00010000|2000000000000000200020000000000000000000000000000000000000000000
:00010020|0000000000002000000020000000000000002000200000000000200020002000
:00010040|0800100010001000000000001000000010000000000000000000000010000000
:00010060|0400040004000000040004000400800000000400000010000000000000001000
:00010080|0000010000000000010000008100000000000000010000000000010001000100
:000100A0|0100010001000000000001000000000000000100000010000000000000001000
:000100C0|0000020000008200000082008200000002000000020000000000000000000000
:000100E0|0200000000000200000002000000000000000200000000001000100010000000
:00010100|0000000000000000000000000000000000000000000000000000000000000000
... (all zeros in between these regions)
:00010C00|F8FFFF0000000000000000000000000000000000000000000000000000000000
... (all zeros in between these regions)
:00010E00|F8FFFF0000000000000000000000000000000000000000000000000000000000
... (all zeros in between these regions)
:00015000|FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
... (all FF's in between these regions)
:0001FE00|0000000000000000000000000000000000000000000000000000000000000000
:0001FE20|0000000000000000000000000000000000000000000000000000000000000000
:0001FE40|0000000000000000000000000000000000000000000000000000000000000000
:0001FE60|0000000000000000000000000000000000000000000000000000000000000000
:0001FE80|0000000000000000000000000000000000000000000000000000000000000000
:0001FEA0|0000000000000000000000000000000000000000000000000000000000000000
:0001FEC0|0000000000000000000000000000000000000000000000000000000000000000
:0001FEE0|0000000000000000000000000000000000000000000000000000000000000000
:0001FF00|0000000000000000000000000000000000000000000000000000000000000000
:0001FF20|0000000000000000000000000000000000000000000000000000000000000000
:0001FF40|0000000000000000000000000000000000000000000000000000000000000000
:0001FF60|0000000000000000000000000000000000000000000000000000000000000000
:0001FF80|0000000000000000000000000000000000000000000000000000000000000000
:0001FFA0|0000000000000000000000000000000000000000000000000000000000000000
:0001FFC0|0000000000000000000000000000000000000000000000000000000000000000
:0001FFE0|0000000000000000000000000000000000000000000000000000000000000000
Any ideas what I might be missing? I also find it slightly odd that the first 20,448 (0x4FE0) bytes is primarily zeros. This memory chip is NAND flash and so I would except all unused space to be FF's. Which makes me think; any space being written to must be erased before being written to (unless the space is already all FF's), but the smallest size I can erase on the SST26V is a sector (4 Kbyte). Does this mean the my Allocation unit size should be 4096 bytes, not 512 bytes?
NOTES
1. I'm using the first 64KB of flash to make debugging easier. Takes too long to print out entire 2MB (8Mbit) of flash over UART.
UPDATE # 1
I think I may have found the problem, but have yet to solve it. When I review all of the read write addresses that are printed out over UART I find multiple instances of Windows writing 512 bytes to address 0x00010000. Seeing as this is NAND flash it must be erased before it can be written to again, but because the smallest amount of memory that can be erased is 4kB I suppose this means that I will have to copy the 4kB flash sector to the STM32's RAM, modify the first 512 bytes, erase the 4kB flash sector, then write what I have in RAM back to the 4kB flash sector. Does that sound correct? Seems inefficient.
UPDATE # 2
I confirmed that my suspicions in UPDATE 1 were correct. I tried using only the first 512 bytes of each sector and it works, but as a result only has 1/8 of the capacity. More updates to follow.
I was able to solve my problem. I was on the correct path in UPDATES 1 & 2.
I was able to use the entire 2MB region of the external flash by creating some new functions that deal with flash memory from a sector perspective and a buffer in RAM that is the size of a sector (4KB). See code below:
/**
* #brief .
* #param lun: .
* #retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 6 */
if((blk_addr * STORAGE_BLK_SIZ + SST26V_MEMORY_OFFSET) == 0x00010000)
printf("\r\nR %d:0x%08X", blk_len*STORAGE_BLK_SIZ, (blk_addr * STORAGE_BLK_SIZ + SST26V_MEMORY_OFFSET)); // print the size and address
_SPI_FLASH_GetData((blk_addr * STORAGE_BLK_SIZ + SST26V_MEMORY_OFFSET), blk_len*STORAGE_BLK_SIZ, buf);
return (USBD_OK);
/* USER CODE END 6 */
}
/**
* #brief .
* #param lun: .
* #retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
uint8_t IsBlockEmptyFlag = 0;
uint8_t temp = 0xFF;
uint16_t sector_number = 0;
uint8_t block_number = 0;
/* USER CODE BEGIN 7 */
printf("\r\nW%d:0x%08X", blk_len*STORAGE_BLK_SIZ, (blk_addr * STORAGE_BLK_SIZ + bytes
// First, check if block that we want to write to is empty (All bytes are 0xFF)
_SPI_FLASH_GetData((blk_addr * STORAGE_BLK_SIZ + SST26V_MEMORY_OFFSET), blk_len*STORAGE_BLK_SIZ, RAMbuffer);
for(uint16_t i = 0; i<512; i++)
temp &= RAMbuffer[i];
if(temp == 0xFF) // then all bytes must be 0xFF and the block is ready to be writen to.
IsBlockEmptyFlag = 1;
if(IsBlockEmptyFlag) // if the block is empty already, have at it, write away
{
_SPI_FLASH_WritePage(blk_len*STORAGE_BLK_SIZ, (blk_addr * STORAGE_BLK_SIZ + SST26V_MEMORY_OFFSET), buf);
}
else // if it's not empty
{
printf("\r\nt=%02X", temp);
// Find the sector number that this block is in
sector_number = blk_addr/8;
// Save the entire flash sector to RAM
_SPI_FLASH_ReadSector(sector_number, RAMbuffer);
// Erase the sector flash sector
SST26V_FLASH_EraseSectorN(sector_number);
// compute the block number of the corresponding sector
block_number = blk_addr % 8;
// create a pointer to the RAM buffer
uint8_t *p_RAM_buffer;
p_RAM_buffer = RAMbuffer;
// Modify the RAM buffer's appropriate block
memcpy(p_RAM_buffer + (512*block_number), buf, blk_len*STORAGE_BLK_SIZ);
// Write the buffer back into external flash
_SPI_FLASH_WriteSector(sector_number, RAMbuffer);
}
return (USBD_OK);
/* USER CODE END 7 */
}
I am trying to test the i2c communication of the MAX77651 chip before programming it.
So here is my setup to do so:
I have an UMFT4222ev connected to my Linux laptop by USB. This chip has his SCL and SDA linked to the SDA and SCL of my MAX77651 thanks to the Evaluation Kit for the MAX77651. My MAX77651evkit is powered with 3,7V on the Vbatt pin.
I also installed the mraa librarie from git hub and the libft4222. I know mraa is well installed because i tried it with and example.
I was told that the mraa library takes in charge the setup of the FT4222 so i only used mraa functions to make my program.
I searched on the website of Maxim integrated the i2c slave address and one register where i could read data and check if everyting is working. I then read the i2c protocol of communication to read a single register which is available here : https://datasheets.maximintegrated.com/en/ds/MAX77650-MAX77651.pdf at the page 78.
With all those informations I tried to make my "test program". I solved the compiling errors but when I execute the program I can't get what is in the register which should be 0xFF.
Here is my program:
#include "stdio.h"
#include "syslog.h"
#include "string.h"
#include "unistd.h"
#include "mraa/i2c.h"
#include "mraa.h"
#define I2C_ADDR 0x48
int
main(int argc, char *argv[])
{
uint8_t *message;
*message=0XAC;
int i,j,k;
char reg_a_lire = 0x06;
mraa_init();
mraa_i2c_context i2c;
i2c = mraa_i2c_init(0);
mraa_i2c_frequency(i2c,MRAA_I2C_FAST);
mraa_i2c_address(i2c, I2C_ADDR);
mraa_i2c_write_byte(i2c,0x90);
mraa_i2c_read(i2c, message,1);
mraa_i2c_write_byte(i2c,reg_a_lire);
mraa_init();
mraa_i2c_write_byte(i2c,0x91);
mraa_i2c_read(i2c, message,1);
mraa_i2c_read(i2c, message,1);
printf("%02X \n", *message);
mraa_i2c_stop(i2c);
return 0;
}
Here is the actual output :
alex#cyclonit-laptop ~/Test_alex/tests $ ./a.out
AC
And i would like to get FF instead of AC.
I think my error could come from something i missed to initialize the FT4222 or from the MAX77651 which I maybe did nt power up correctly and its not sufficient to put 3,7V on Vbatt. But maybe this is a problem with my program because i don't know much about uint8_t and I made something wrong.
I am hoping someone has experience with FT4222 and/or MAX77651 and can help me.
I think you are confused by pg. 75 of the MAX77651 datasheet.
ADDRESS | 7-BIT SLAVE ADDRESS | 8-BIT WRITE ADDRESS | 8-BIT READ ADDRESS
Main Address | 0x48, 0b 100 1000 | 0x90, 0b 1001 0000 | 0x91, 0b 1001 0001
(ADDR = 1)* | | |
-------------+---------------------+---------------------+-------------------
Main Address | 0x40, 0b 100 0000 | 0x80, 0b 1000 0000 | 0x81, 0b 1000 0001
(ADDR = 0)* | | |
Depending on your view of the I²C specification, you either use the 7-bit address or use both 8-bit addresses. The reason is that the first I²C transaction sends the following 8 bit:
76543210
\_____/\-- R/#W
\------ 7-bit Slave Address
In your case that's 0x48 shifted one bit to the left followed by either a 1 for read mode or a 0 for write mode. Notice that
(0x40 << 1) | 0x00 == 0x80 == 8-bit Write Address (ADDR = 0)
(0x40 << 1) | 0x01 == 0x81 == 8-bit Read Address (ADDR = 0)
(0x48 << 1) | 0x00 == 0x90 == 8-bit Write Address (ADDR = 1)
(0x48 << 1) | 0x01 == 0x91 == 8-bit Read Address (ADDR = 1)
Some people like to use the slave address while other people prefer to give separate addresses so it's clear what the first byte of communication will look like.
So you should remove the extraneous mraa_i2c_write_byte(i2c, ...); lines from your code, as their intention seems to be to send the read or write address to the slave.
Check out the MRAA library API reference. There are functions that abstract reading and writing registers, either byte-wide [8-bit] registers or word-wide [16-bit] registers:
mraa_i2c_read_byte_data
mraa_i2c_read_bytes_data
mraa_i2c_write_byte_data
In both cases you should check the return codes to know if the action succeeded. In your case, your message is likely not altered, because there was a stop/error condition beforehand, because the slave did either not ACK its slave address or it did not ACK the 0x90/0x91 data bytes you mistakenly sent, as they don't show up in the Programmer's Guide as valid addresses.
Another issue is that you try to read register INTM_GLBL (0x06; Global Interrupt Mask Register) using two consecutive read operations. I'm not sure if your intention is to read register 0x06 twice or if you intended to read INT_M_CHG (0x07; Global Interrupt Mask for Charger), because that's not what it will do. Notice the description on pg. 78 of the datasheet:
Note that when the MAX77650/MAX77651 receive a stop
they do not modify their register pointer.
This is typical behavior for I²C slaves that support multi-byte/sequential reads. You will have to issue a sequential read operation for multiple bytes of data if you want to read multiple registers, e.g. using mraa_i2c_read_bytes_data.
Something like the following might get you on the right track. It is supposed to read the CID and CLKS settings and wait forever if a read error occurred. If successful, it will disable charging, all outputs, setup the red LED to blink once every other second and -- as soon as you uncomment it -- transition the On/Off Controller into On Via Software state to enable bias and the LED.
#define MAX77651_SLA 0x48u
#define CNFG_GLBL_REG 0x10u
#define CID_REG 0x11u
#define CNFG_CHG_B_REG 0x19u
#define CNFG_SBB0_B_REG 0x2Au
#define CNFG_SBB1_B_REG 0x2Cu
#define CNFG_SBB2_B_REG 0x2Eu
#define CNFG_LDO_B_REG 0x39u
#define CNFG_LED1_A_REG 0x41u
#define CNFG_LED1_B_REG 0x44u
#define CNFG_LED_TOP_REG 0x46u
int main(int argc, char *argv[]) {
uint8_t data;
int res;
mraa_init();
mraa_i2c_context i2c0;
i2c0 = mraa_i2c_init(0);
mraa_i2c_frequency(i2c0, MRAA_I2C_STD);
mraa_i2c_address(i2c0, MAX77651_SLA);
res = mraa_i2c_read_byte_data(i2c0, CID_REG);
if (res < 0) {
printf("Reading CID_REG failed.\n");
mraa_i2c_stop(i2c0);
while(1);
}
data = res;
printf("CID_REG: CLKS = %02X CID = %02X\n", ((data & 0x70u) >> 4), (data & 0x0Fu));
/* you should check return values here */
mraa_i2c_write_byte_data(i2c0, 0x00u /* CHG_EN = off */, CNFG_CHG_B_REG);
mraa_i2c_write_byte_data(i2c0, 0x04u /* EN_LDO = off */, CNFG_LDO_B_REG);
mraa_i2c_write_byte_data(i2c0, 0x04u /* EN_SBB0 = off */, CNFG_SBB0_B_REG);
mraa_i2c_write_byte_data(i2c0, 0x04u /* EN_SBB1 = off */, CNFG_SBB1_B_REG);
mraa_i2c_write_byte_data(i2c0, 0x04u /* EN_SBB2 = off */, CNFG_SBB2_B_REG);
/* set up red led to toggle every second */
mraa_i2c_write_byte_data(i2c0, 0x17u /* P = 1s, D = 50% */, CNFG_LED1_B_REG);
mraa_i2c_write_byte_data(i2c0, 0x98u /* FS = 6.4mA, BRT = 5.0mA */, CNFG_LED1_A_REG);
mraa_i2c_write_byte_data(i2c0, 0x01u /* EN_LED_MSTR = on */, CNFG_LED_TOP_REG);
// DANGER ZONE: enable only when you know what this is supposed to do
//mraa_i2c_write_byte_data(i2c0, 0x10u /* SBIA_EN = on, SBIA_LPM = normal */, CNFG_GLBL_REG);
mraa_i2c_stop(i2c0);
while(1);
}
I managed to read I2C registers of MAX77651.
First looking at the hardware part I had to make sure that VIO had the right voltage like #FRob said in hid comment.
Then for the software, I stopped using the mraa library because i could'nt control everything. I used the FT4222 library which allowed me to open and initiate the FT4222 device. I took some part of the I2C example in this library for the initialization of the device. Then I noticed that I had to first use the FT4222's write function to send the register i wanted to read , then simply use the read function to get my result. Those are the two steps required.
I won't post the whole program i made as it is mainly taken from the I2C example for initialization but here is the part I added to read my register 0X06 which is supposed to contain 0xFF:
uint8 resultat=0x11;
uint8 *p_resultat=&resultat;
int chiffre;
slaveAddr
uint16 bytesToWrite2 = 1;
uint16 bytesWritten2=1;
uint8 valeur= 0x06; // REGISTER TO READ
uint8 *p_valeur=&valeur;
FT4222_I2CMaster_Write(ftHandle,slaveAddr,
p_valeur,bytesToWrite2,&bytesWritten); //INDICATES WHICH REGISTER TO
// READ
chiffre = FT4222_I2CMaster_Read(ftHandle,
slaveAddr,p_resultat,1, &bytesRead); // READ REGISTER
printf("The content of the register %02X is : %02X \n " ,
valeur,resultat); // DISPLAY RESULT
printf("reading success if : %d = 0 \n " , chiffre);
// READING SUCCESS ?
With this code and the initialization i get the following result :
alex#cyclonit-laptop ~/Downloads/libft4222-1.2.1.4/examples $ ./a.out
Device 0 is interface A of mode-0 FT4222H:
0x0403601c FT4222 A
Chip version: 42220200, LibFT4222 version: 01020104
The content of the register 06 is : FF
reading success if : 0 = 0
Skipping interface B of mode-0 FT4222H.
If someone has the same problem you are free to ask me your question by answering this post.I am very thankful to the people here who helped me!
I'm facing a problem with my erasing function.
I try to erase 15 sectors of my Flash in order to put a new binary file.
I don't understand why but my function freeze and i cannot erase all memory i need.
Here is my code if you want to give a try
/*
* bootloader.c
*
* Created on: 9 juin 2015
* Author: tgloaguen
*/
#include "usart.h"
#include "stm32l1xx_flash.h"
#define WRITE_START_ADDR 0x08000000
#define WRITE_END_ADDR 0x0800FFFF
#define FLASH_PAGE_SIZE ((uint16_t)0x100) //If a page is 256 bits
#define MY_BL_FUNCTIONS __attribute__((section(".bootsection")))
void BootLoader(void) MY_BL_FUNCTIONS;
FLASH_Status Flash_Write ( uint32_t StartAddress, uint8_t *p, uint32_t Size ) MY_BL_FUNCTIONS;
uint8_t Flash_Erase() MY_BL_FUNCTIONS;
void Receive_Data(char * buffer,int size)MY_BL_FUNCTIONS;
void Receive_Size(char * buffer, int *sizeData)MY_BL_FUNCTIONS;
void BootLoader(void) {
//clear all ITs
USART_ITConfig( USART1, USART_IT_RXNE, DISABLE );
//SendString("HELLO",USART2);
uint8_t status,i;
char buffer[33];
//en dur
uint16_t *adr = WRITE_START_ADDR;
uint16_t sizeBin = 51400,k = 0,k_hexa = 0x20;
SendString("BOOTLOADER",USART2);
Flash_Erase();
SendString("ERASEOK",USART2);
//if sizeBin ok
}
and the erase function
uint8_t Flash_Erase() {
uint32_t EraseCounter = 0x00, Address = 0x00;//Erase count, write address
uint32_t NbrOfPage = 0x00;//Recording to erase the pages
volatile FLASH_Status FLASHStatus = FLASH_COMPLETE;/*FLASH complete erasure marks*/
/*Unlock FLASH*/
FLASH_Unlock();
/*Calculate the number of FLASH pages need to erase */
NbrOfPage = (WRITE_END_ADDR - WRITE_START_ADDR) / FLASH_PAGE_SIZE;
/* Remove all hang flags */
FLASH_ClearFlag ( FLASH_FLAG_EOP |
FLASH_FLAG_WRPERR |
FLASH_FLAG_PGAERR |
FLASH_FLAG_SIZERR |
FLASH_FLAG_OPTVERR );
/* Erase the FLASH page*/
for(EraseCounter = 0; (EraseCounter <NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)
{
SendString("ok |",USART2);
FLASHStatus = FLASH_ErasePage(WRITE_START_ADDR + (FLASH_PAGE_SIZE * EraseCounter));
}
FLASH_Lock ( );
return (uint8_t)FLASHStatus;
}
In your code you are tring to erase the whole flash, but you are executing your bootloader from flash from final sectors.
As reported by th REF man
During a write/erase operation to the NVM (except Half Page programming or Double-word
erase/write), any attempt to read the same bank of NVM stalls the bus.
Then you have to preserve the space of bootloader changing WRITE_END_ADDR define according to your memory map.
Example: if your bootloader is 4K long and belongs to the last sector (0x0801 F000 - 0x0801 FFFF) then WRITE_END_ADDR must be 0x0801 EFFF.
EDIT
As #Olaf wrote take care about your ISP (Initial Stack Pointer) and IPC (Initial Program Counter) that belong to first 8 bytes in your flash at address 0.
I want to store a number to PIC18 then retain it even if the power is lost or the unit is reset. I think my writing code portion looks fine, just the reading portion of it looks strange after the unit is reset.
I am using the following code which I got from Microchip.
Code:
unsigned int value;
unsigned int DEEdata = 1;
unsigned int DEEaddr = 0x04;
DataEEInit();
dataEEFlags.val = 0;
DataEEWrite(DEEdata,DEEaddr);
value = DataEERead(DEEaddr);
Nop();
printf("%d",value);
The ouput: 1
However when I reset the unit and only use the reading code I always get 255.
Code to read:
DataEEInit();
value = DataEERead(DEEaddr);
printf("%d",value);
The output: 255
Why is this happening? I am assuming maybe the value is not being saved or the reading portion is incorrect. Thank you!
Two functions: write to flash using 64-byte buffer # 8-byte blocks and a read/compare flash function.
For device: PIC18F46K80
Stuff for a header file:
#define PRGM_BUFFER_SIZE 8
#define TABLE_WRITE_SIZE 64
#define LOAD_TBL_PTR(x) { TBLPTRU = ((((x)>>8)>>8)&0xff);\
TBLPTRH = (((x) >> 8) & 0xff);\
TBLPTRL = ((x) & 0xff);\
}
Write to flash function:
/******************************************************
* Function : write_block
* Input : uint16_t position in destination flash
* Global : uint8_t buffer[64] - Location of source data
* Output : None
* Description : Writes the contents of the 64 byte
* data buffer to program flash space. Only 64 bytes
* can be written at once. The process of writing
* to flash is: Erase->Write.
******************************************************/
static void write_block(uint16_t addr)
{
int r, c;
// Erase flash block first. Erases a 64 byte block at a time.
LOAD_TBL_PTR(addr);
EECON1bits.EEPGD = 1; // Point to flash program memory
EECON1bits.CFGS = 0; // Access flash memory
EECON1bits.WREN = 1; // Enable write to memory
EECON1bits.FREE = 1; // Enable Erase operation
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1; // Clear the flash
asm("NOP"); // Stall
// Write buffer to internal buffer. This process writes 8 bytes at a time
// so we need to loop 8 times (8*8 = 64).)
for (r = 0; r < 8; r++)
{
LOAD_TBL_PTR((addr + (r * 8)));
for (c = 0; c < PRGM_BUFFER_SIZE; c++)
{
TABLAT = buffer[(r * 8) + c];
asm("TBLWT*+"); // Push byte and then inc to next internal buffer cell
}
// Write the block to flash
asm("TBLRD*-"); // Point back to original row
// Write internal buffer to flash
EECON1bits.EEPGD = 1; // Point to flash program memory
EECON1bits.CFGS = 0; // Access flash program memory
EECON1bits.WREN = 1; // Enable write to memory
INTCONbits.GIE = 0; // Disable interrupts
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1; // Start programming flash
INTCONbits.GIE = 1; // Re-enable interrupts
EECON1bits.WREN = 0; // Disable write to memory
}
}
Verify written data (demonstrates flash read)
/******************************************************
* Function : compare_block
* Input : uint16_t position in destination flash
* Global : uint8_t buffer[64] - Location of previous written data
* Output : bool true=successful, false=did not match
* Description : Reads a 64 byte block of flash memory and
* compares it to the data found in the global buffer.
******************************************************/
static bool compare_block(uint16_t addr)
{
bool retVal = true; // succeeds
uint8_t i = 0;
INTCONbits.GIE = 0; // Disable interrupts
LOAD_TBL_PTR(addr);
for (i = 0; i < TABLE_WRITE_SIZE && retVal == true; i++)
{
asm("TBLRD*+");
if (buffer[i] != TABLAT)
retVal = false;
}
INTCONbits.GIE = 1; // Enable interrupts
return retVal;
}
Yours,
Bryan Wilcutt
The device you're using doesn't have internal non-volatile memory apart from its Flash, generally used for storing code.
You have two options that I can see:
Use some external Flash or EEPROM and interface it to the External Memory Bus that's available on this PIC (see page 97 of the Family Datasheet).
Remap the internal Flash to reserve a small portion that can be used for storing your data (so that it doesn't interfere with the memory area used exclusively for code) and write your data into this region (page 87).
I haven't worked with PICs for years, so can't offer you much in the way of implementation detail but I suspect there are many examples you can source from Microchip's website.
In essence, the reason your code doesn't work is because you're trying to access memory that isn't there. If it is there, then the interface is not correct.
EDIT:
I've had a look through the code examples page for the PIC18 on Microchip's website and can't find any C examples for writing to the program memory. Unfortunately, it looks like you'll have to implement it in assembler. I don't know the semantics for the MPLAB compiler but, generally, it'll be something like this if you're going to do it inline:
void my_assembler_function(void)
{
// Inline assembler code, actioned via C.
asm("MOV x y");
asm("MOV y z");
}
Alternatively, many C compilers for microprocessor's allow you to call an external .s file with a C function call, saving you from doing it inline.
I think you can follow the example I found here to actually implement the functionality you're after.
SRAM can not be used to store Non-volatile data...
SRAM will loose data during power cycle...
Options:
1. Use internal EEPROM if available.
2. External EEPROM through I2C or SPI.
3. PIC18 Data Emulation Library.
This is an explanation of the PIC18:
/* EEPROM Read and Write Functions -- WORKING
* Used PIC18F45K22 and MPLAB and C18
* Read and Write functions work.
* EEPROM has 256 bytes of memory (256 distinct characters)
* Select "Window" -> "PIC Memory Views" -> "EE Data Memory"
* Download program to PIC18
* Hold PIC in Reset (circle arrow with pause button)
* Open EE Data Memory Tab and click "Read Device Memory" button (Top left of EE Data tab) while PIC is held in Reset
*/
This is helpful code:
#include <p18cxxx.h>
#include <p18f45k22.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#pragma config FOSC = INTIO67, PLLCFG = OFF, PRICLKEN = ON, FCMEN = ON, PWRTEN = OFF
#pragma config BOREN = SBORDIS, BORV = 250, WDTEN = OFF, WDTPS = 2048, PBADEN = OFF, WRTD = OFF
#pragma config HFOFST = OFF, MCLRE = EXTMCLR, STVREN = ON, LVP = OFF, DEBUG = ON, CPD = OFF
void EE_Write(unsigned char addr, unsigned char value);
unsigned char EE_Read(unsigned char addr);
unsigned char test;
void main(void){
OSCTUNEbits.PLLEN = 1;
OSCCON = 0x4C; //Set to use internal clock.
OSCCON2 = 0x00; // No 4x PLL
TRISB = 0x00;
ANSELB = 0x00;
PORTB = 0x00;
EE_Write(05, 0x5A);
Delay10KTCYx(50);
test = EE_Read(05);
Delay10KTCYx(50);
PORTB = 0xFF;
Delay10KTCYx(50);
PORTB = 0x00;
}
void EE_Write(unsigned char addr, unsigned char value)
{
unsigned char SaveGIE = 0;
// Set EEPROM address
EEADR = addr%256;
// Set EEPROM data
EEDATA = value;
// Select Data
EECON1bits.EEPGD = 0;
// Select EEPROM
EECON1bits.CFGS = 0;
// Enable write
EECON1bits.WREN = 1;
// Save current global interrupt enable state
SaveGIE = INTCONbits.GIE;
// Disable interrupts
INTCONbits.GIE = 0;
// Write unlock sequence
EECON2 = 0x55;
EECON2 = 0xaa;
// Start write
EECON1bits.WR = 1;
// Restore previous interrupts enable state
INTCONbits.GIE = SaveGIE;
// Wait for write completion
while(EECON1bits.WR);
// Disable writes
EECON1bits.WREN = 0;
}
unsigned char EE_Read(unsigned char addr){
while(EECON1bits.RD || EECON1bits.WR); // check the WR&RD bit to see if a RD/WR is in progress
EEADR = addr; // Write the address to EEADR.
EECON1bits.CFGS = 0;
EECON1bits.EEPGD = 0;
EECON1bits.RD = 1; // Set the RD bit to trigger the eeprom read operation.
return(EEDATA);
}
Some PIC18 micros have an internal EEPROM in addition to the internal flash. The 18F87J11 does not have this so you have 2 options:
1) Write to the flash memory - this is where your program is stored. make sure the number of write/read cycles is ok for your application.
2) Use an external i2c or spi memory for your configuration settings
The DataEEWrite you are using are from an 'eeprom emulation' library from microchip (linked in the comments below. There are a couple of things to be careful of:
Watch out when reprogramming the flash! you might overwrite your settings
Remember it isn't really eeprom! The write cycles are limited and you have to erase big sections of memory - you can't erase a single byte
The value of 255 is default value for EEPROM memory. I think after changing the code, you program microcontroller IC again. So, your EEPROM memory will be erased and return to its default value. If you use MPLAB as compiler, you can go to 'Programmer'tab > Settings.. > Program Memory > Program Options and click on Preserve EEPROM on Program.
Hope it works.
To retain the values on power cycle, SRAM memory should be used. Please confirm if you have SRAM memory available first.
I enabled MMU on my s3c2440 board (3G - 4G memory :: the fault attribute),everything was just fine when I didn't read/write 3G - 4G memory .So to test the page fault vector ,I wrote to a 0xFF to the 3G address,as I expected ,I got the right value from FSR ,So I did this in _do_page_fault (), the step was like this :
..... // set new page to translation table
.....
invlidate_icache (); // clear icache
clr_dcache (); // wb is used ,clear dcache
invalidate_ttb (); // invalidate translation table
and then ISR_dataabort returned ,I read the 3G address to get the 0xFF which I wote before .
Unfortunately I got a data abort again .(I am sure the translation table value I set is ok)
So what is the correct way to update the MMU translation table .Any help is appreciate ! thanks
here is the Main code I used (just for some test),
(I am a littlte strange to ARM ARCH,so this code might be urgly )
/* MMU TTB 0 BASE ATTR */
#define TTB0_FAULT (0|(1<<4)) /* TTB FAULT */
#define TTB0_COARSE (1|(1<<4)) /* COARSE PAGE BASE ADDR */
#define TTB0_SEG (2|(1<<4)) /* SEG BASE ADDR */
#define TTB0_FINE (3|(1<<4)) /* FINE PAGE BASE ADDR */
/* MMU TTB 1 BASE ATTR */
#define TTB1_FAULT (0)
#define TTB1_LPG (1) /* Large page */
#define TTB1_SPG (2) /* small page */
#define TTB1_TPG (3) /* tiny page */
/* domain access priority level */
#define FAULT_PL (0x0) /* domain fault */
#define USR_PL (0x1) /* usr mode */
#define RSV_PL (0x2) /* reserved */
#define SYS_PL (0x3) /* sys mode */
#define DOMAIN_FAULT (0x0<<5) /* fault 0*/
#define DOMAIN_SYS (0x1<<5) /* sys 1*/
#define DOMAIN_USR (0x2<<5) /* usr 2*/
/* C,B bit */
#define CB (3<<2) /* cache_on, write_back */
#define CNB (2<<2) /* cache_on, write_through */
#define NCB (1<<2) /* cache_off,WR_BUF on */
#define NCNB (0<<2) /* cache_off,WR_BUF off */
/* ap 2 bits */
#define AP_FAULT (0<<10) /* access deny */
#define AP_SU_ONLY (1<<10) /* rw su only */
#define AP_USR_RO (2<<10) /* sup=RW, user=RO */
#define AP_RW (3<<10) /* su=RW, user=RW */
/* page dir 1 ap0 */
#define AP0_SU_ONLY (1<<4) /* rw su only */
#define AP0_USR_RO (2<<4) /* sup=RW, user=RO */
#define AP0_RW (3<<4) /* su=RW, user=RW */
/* page dir 1 ap1 */
#define AP1_SU_ONLY (1<<6) /* rw su only */
#define AP1_USR_RO (2<<6) /* sup=RW, user=RO */
#define AP1_RW (3<<6) /* su=RW, user=RW */
/* page dir 1 ap2 */
#define AP2_SU_ONLY (1<<8) /* rw su only */
#define AP2_USR_RO (2<<8) /* sup=RW, user=RO */
#define AP2_RW (3<<8) /* su=RW, user=RW */
/* page dir 1 ap3 */
#define AP3_SU_ONLY (1<<10) /* rw su only */
#define AP3_USR_RO (2<<10) /* sup=RW, user=RO */
#define AP3_RW (3<<10) /* su=RW, user=RW */
#define RAM_START (0x30000000)
#define KERNEL_ENTRY (0x30300000) /* BANK 6 (3M) */
#define KERNEL_STACK (0x3001A000) /* BANK 6 (16K + 64K + 16K + 8K) (8k kernel stack) */
#define IRQ_STACK (0x3001B000) /* 4K IRQ STACK */
#define KERNEL_IMG_SIZE (0x20000)
#define IRQ_STACK (0x3001B000)
/* 16K aignment */
#define TTB_BASE (0x30000000)
#define PAGE_DIR0 (TTB_BASE)
#define TTB_FULL_SIZE (0x4000)
#define PAGE_DIR1 (TTB_BASE+TTB_FULL_SIZE)
#define PAGE_DIR0_SIZE (0x4000) /* 16k */
void _do_page_fault (void)
{
//
...........
//
// read the FSR && get the vaddr && type here
volatile unsigned *page_dir = (volatile unsigned*)(TTB_BASE);
unsigned index = vaddr >> 20,i = 0, j = 0;
unsigned page = 0;
if (!(page_dir[index] & ~(0x3FF) && (type == 0x0B))) { /* page_dir empty */
i = index & ~0x03;
if ( (page_dir[i+0] & ~(0x3FF)) || (page_dir [i+1] & ~(0x3FF))
|| (page_dir[i+2] & ~(0x3FF)) || (page_dir [i+3] & ~(0x3FF)) )
{
panic ( "page dir is bad !\n" ); /* 4 continuous page_dir must be 0 */
}
if (!(page = find_free_page ()))
panic ( "no more free page !\n" ); /* alloc a page page dir*/
page_dir[i+0] = (page + 0x000) | DOMAIN_USR | TTB0_COARSE ; /* small page 1st 1KB */
page_dir[i+1] = (page + 0x400) | DOMAIN_USR | TTB0_COARSE ; /* small page 2nd 1KB */
page_dir[i+2] = (page + 0x800) | DOMAIN_USR | TTB0_COARSE ; /* small page 3rd 1KB */
page_dir[i+3] = (page + 0xC00) | DOMAIN_USR | TTB0_COARSE ; /* small page 4th 1KB */
if (!(page = find_free_page ()))
panic ( "no more free page !\n" ); /* alloc a page page table*/
volatile unsigned *page_tbl = (volatile unsigned*) (page_dir[index] & ~(0x3FF));
*page_tbl = page|AP0_RW|AP1_RW|AP2_RW|AP3_RW| NCNB|TTB1_SPG;/* small page is used */
invalidate_icache ();
for (i = 0; i < 64; i++)
{
for (j = 0;j < 8;j ++)
clr_invalidate_dcache ( (i<<26)|(j<<5) );
}
invalidate_tlb ();
}
........
//
}
/* here is the macros */
#define invalidate_tlb() \
{\
__asm__ __volatile__ (\
"mov r0,#0\n"\
"mcr p15,0,r0,c8,c7,0\n"\
:::"r0" \
);\
}
#define clr_invalidate_dcache(index) \
{\
__asm__ __volatile__ (\
"mcr p15,0,%[i],c7,c14,2\n"\
:: [i]"r"(index)\
);\
}
#define invalidate_icache() \
{\
__asm__ __volatile__ (\
"mov r0,#0\n"\
"mcr p15,0,r0,c7,c5,0\n"\
::: "r0"\
);\
}
#define invalidate_dcache() \
{\
__asm__ __volatile__ (\
"mov r0,#0\n"\
"mcr p15,0,r0,c7,c6,0\n"\
::: "r0"\
);\
}
#define invalidate_idcache() \
{\
__asm__ __volatile__ (\
"mov r0,#0\n"\
"mcr p15,0,r0,c7,c7,0\n"\
:::"r0"\
);\
}\
Note: I assume TTB_BASE is the primary ARM L1 page table. If it is some shadow, you need to show more code as per unixsmurf. Here is my best guess...
Your page_dir is functioning as both the primary L1 entries and as the L2 fine page table. The TTB_BASE should only contain sections, super sections or pointers to sub-page tables. You need to allocate more physical memory for the L2 page tables.
I guess your page_dir[i+0], page_dir[i+1], etc are overwriting other L1 section entries and Your invalidate_tlb() make this concrete to the CPU. You should be using the L2 page_tbl pointer to set/index the small/fine pages. Perhaps your kernel code is in the 3G-4G space and your are over-writing some critical L1 mappings there; any number of strange things could happen. The current use of page_tbl is unclear.
You can not double use the primary L1 tables as they have meaning to hardware. I guess that this is just a mistake?
There are only three types of entries in the primary L1 page tables,
Sections - a 1MB memory mapping straight virt to phys, with no 2nd page table.
Super-sections - a 4MB memory mapping as per sections, but minimizing TLB pressure.
A 2nd page table. Entries can be 1k,4k,and 64k. Fine page tables are 4k in table size and are not in modern ARM designs. Each entry is 1k of address in a fine page table.
The primary page table must be on a physical address that is 16k aligned, which is also it's size. 16k/(4bytes/entry)*1MB gives a 4GB address range of the L1 tables. So each entry in the primary L1 page table always refers to a 1MB entry. Coarse page tables are the only option on newer ARMs and they refer to a 1K L2 table.
1K_L2_size * 4K_entry / (4bytes_per_entry) gives 1MB address space.
1K_L2_size * 64K_entry / (16bytes_per_entry) gives 1MB address space.
There are four entries of 1MB each in the primary L1 for a super-section. There are four entries each for a 64k large page in the L2 table. If you are using super-sections, you do not have L2 entries.
I think you may have Super-sections and large pages mixed up? For certain having improperly formatted page tables will only manifest on a TLB invalidate, so that MMU mappings are re-fetched from the tables via a walk.
Finally, you should flush the D cache and drain the write buffer to ensure consistency with memory. You may wish to have a memory barrier as well.
static inline void dcache_clean(void)
{
const int zero = 0;
asm volatile ("" ::: "memory"); /* barrier */
/* clean entire D cache -> push to external memory. */
asm volatile ("1: mrc p15, 0, r15, c7, c10, 3\n"
" bne 1b\n" ::: "cc");
/* drain the write buffer */
asm volatile ("mcr 15, 0, %0, c7, c10, 4"::"r" (zero));
}
There are also co-processor commands to invalidate single TLB entries as you are only changing a portion of the vaddr/paddr mappings in the data fault.
See also: ARM MMU tutorial, Virtual Memory structures, and your ARM Architecture Reference Manual.