有没有达到可编程的水平,我前面这么多例子就是啊...
我贴的都是无删减的原始对话。你可以看看我才说了多少句话,大部分都是对着sdk/example ctrl+c/v。
然后事实上我现在就靠搬运chatgpt的结果进行板卡调试。昨天发帖的时候compile就通过了,今天我在增加更多的功能,需要用flash存储点东西,例子附在最后。你可以看看我从完全不知道相关信息开始,问了几个问题实现了功能...
我现在是觉得只要能用coding形式完成的东西,就可以交给ai去干,不局限于mcu编程。我前面说了很快会用skidl来尝试让chatgpt做板级原理图设计。我也很想知道它能干成啥样,但如果干得好,那再加个ai router,以后硬件的工作量就真的大幅度简化了。
a53和m0都有各自的用处,f1c200s很早就1刀以下了,也没见哪个mcu要被取代啊。
推荐新人从基础学起还是从arduino搞起,我的观点是如果要认真学,后面要长期从事这个行业,那就得从打基础开始一步步搞。但如果只是想随便找个解决问题的工具,那自然是怎么简单怎么好。两个选择并不冲突。哪怕是后ai时代,如果人类自己失去了从底层开始理解一切细节的能力,凡事都只能听ai的,那人类整个就被ai架空了,鬼知道后面会发生什么。虽然对于这个未来我比较悲观,但最起码在现在还是应该尽量培养尽量多的,具备底层能力的人比较好。未来真要反抗天网,就靠他们了....
===============
## You
有没有特别针对nand flash,代码footprint比较小并且成熟的开源实现?
## ChatGPT
在嵌入式系统中,专为NAND Flash设计的开源文件系统中,JFFS2 (Journaling Flash File System 2) 和 YAFFS (Yet Another Flash File System) 是最知名的。然而,这两者对于资源有限的MCU来说,可能有点过于复杂,代码footprint相对较大。
相对来说,UBI/UBIFS (Unsorted Block Images/Unsorted Block Image File System) 是另一种适合NAND Flash的文件系统,它提供了一种简单的wear-leveling策略,可以有效处理NAND Flash的坏块,并且实现相对简单。
但是,请注意,由于NAND Flash的物理特性,包括需要处理坏块,需要大块的擦除和写入,需要Error Checking and Correcting (ECC) 等,针对NAND Flash的文件系统一般需要更复杂的处理,代码footprint可能会相对较大。
因此,如果你的系统资源非常有限,可以考虑使用基于FAT文件系统的Chan's FatFs + wear leveling策略,如Arduino的SdFat库就是这种实现,尽管这不是针对NAND Flash的最佳实践,但在一些不太关注Flash寿命,不需要处理大量写入/擦除操作的应用中,这种实现可能更简单,代码footprint更小。
总的来说,每种实现都有其利弊,需要根据你的具体需求和系统资源来权衡。
## You
也不需要文件系统这么复杂,有没有针对nand的wear leveling的key-value store库呢?
## ChatGPT
对于相对简单的 Key-Value 存储系统并且有 Wear Leveling 需求的情况,你可以看看以下这些开源库:
1. LittleFS:这是一个用于微控制器的文件系统,提供了Wear Leveling、动态元数据等特性。它设计得很轻巧,并且非常适用于对存储要求较高的微控制器系统。你可以把它当做一个键值存储系统使用。
2. nvs (non-volatile storage):这是一个简单的键值存储库,由 Espressif Systems 开发,专为 ESP32 设备设计,包含 Wear Leveling 特性。
3. SPIFFS (SPI Flash File System):它是一个专为 SPI NOR Flash 设计的轻量级文件系统,但也可以被用作简单的 Key-Value 存储系统,尽管它没有明确的 Wear Leveling 功能,但是它有一些设计能够在一定程度上提供类似的效果。
4. RocksDB: 这是由 Facebook 开发的一种持久性 Key-Value 存储,其可以适应各种各样的硬件设定。这个库相对较大,但提供了高级的 Wear Leveling 和其他优化。
请注意,以上这些库可能需要进行一些适配和配置工作,才能在你的特定硬件和 NAND Flash 上运行。并且它们也都有各自的利弊,例如代码大小、复杂性、功能性、效率等方面,你需要根据你的具体需求和条件来选择。
## You
这是一个canopennode的storage模板:
#if (CO_CONFIG_STORAGE) & CO_CONFIG_STORAGE_ENABLE
/*
* Function for writing data on "Store parameters" command - OD object 1010
*
* For more information see file CO_storage.h, CO_storage_entry_t.
*/
static ODR_t storeLittleFS(CO_storage_entry_t *entry, CO_CANmodule_t *CANmodule) {
/* Open a file and write data to it */
/* file = open(entry->pathToFileOrPointerToMemory); */
CO_LOCK_OD(CANmodule);
/* write(entry->addr, entry->len, file); */
CO_UNLOCK_OD(CANmodule);
return ODR_OK;
}
/*
* Function for restoring data on "Restore default parameters" command - OD 1011
*
* For more information see file CO_storage.h, CO_storage_entry_t.
*/
static ODR_t restoreLittleFS(CO_storage_entry_t *entry,
CO_CANmodule_t *CANmodule) {
/* disable (delete) the file, so default values will stay after startup */
return ODR_OK;
}
CO_ReturnError_t CO_storageLittleFS_init(CO_storage_t *storage,
CO_CANmodule_t *CANmodule,
OD_entry_t *OD_1010_StoreParameters,
OD_entry_t *OD_1011_RestoreDefaultParam,
CO_storage_entry_t *entries,
uint8_t entriesCount,
uint32_t *storageInitError) {
CO_ReturnError_t ret;
/* verify arguments */
if (storage == NULL || entries == NULL || entriesCount == 0 ||
storageInitError == NULL) {
return CO_ERROR_ILLEGAL_ARGUMENT;
}
/* initialize storage and OD extensions */
ret = CO_storage_init(storage, CANmodule, OD_1010_StoreParameters,
OD_1011_RestoreDefaultParam, storeBlank, restoreBlank,
entries, entriesCount);
if (ret != CO_ERROR_NO) {
return ret;
}
/* initialize entries */
*storageInitError = 0;
for (uint8_t i = 0; i < entriesCount; i++) {
CO_storage_entry_t *entry = &entries[i];
/* verify arguments */
if (entry->addr == NULL || entry->len == 0 || entry->subIndexOD < 2) {
*storageInitError = i;
return CO_ERROR_ILLEGAL_ARGUMENT;
}
/* Open a file and read data from file to entry->addr */
/* file = open(entry->pathToFileOrPointerToMemory); */
/* read(entry->addr, entry->len, file); */
}
return ret;
}
然后这是littlefs的官方使用范例:
#include "lfs.h"
// variables used by the filesystem
lfs_t lfs;
lfs_file_t file;
// configuration of the filesystem is provided by this struct
const struct lfs_config cfg = {
// block device operations
.read = user_provided_block_device_read,
.prog = user_provided_block_device_prog,
.erase = user_provided_block_device_erase,
.sync = user_provided_block_device_sync,
// block device configuration
.read_size = 16,
.prog_size = 16,
.block_size = 4096,
.block_count = 128,
.cache_size = 16,
.lookahead_size = 16,
.block_cycles = 500,
};
// entry point
int main(void) {
// mount the filesystem
int err = lfs_mount(&lfs, &cfg);
// reformat if we can't mount the filesystem
// this should only happen on the first boot
if (err) {
lfs_format(&lfs, &cfg);
lfs_mount(&lfs, &cfg);
}
// read current count
uint32_t boot_count = 0;
lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));
// update boot count
boot_count += 1;
lfs_file_rewind(&lfs, &file);
lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));
// remember the storage is not updated until the file is closed successfully
lfs_file_close(&lfs, &file);
// release any resources we were using
lfs_unmount(&lfs);
// print the boot count
printf("boot_count: %d\n", boot_count);
}
请根据littlefs的范例,把storagelittlefs修改成能用的样子
## ChatGPT
在修改代码之前,我们需要做一些假设:
- 假设你已经定义了和 user_provided_block_device_* 相同的函数:block_device_read, block_device_prog, block_device_erase, block_device_sync。 这些函数应该是针对你的硬件的实现。
- 假设 pathToFileOrPointerToMemory 是一个字符串,表示在 LittleFS 上的文件路径。
这是一个可能的修改版,但它可能需要进一步的修改以适应你的环境和硬件。
```c
#include "lfs.h"
// Configuration of the filesystem
const struct lfs_config cfg = {
.read = block_device_read,
.prog = block_device_prog,
.erase = block_device_erase,
.sync = block_device_sync,
.read_size = 16,
.prog_size = 16,
.block_size = 4096,
.block_count = 128,
.cache_size = 16,
.lookahead_size = 16,
.block_cycles = 500,
};
// LittleFS filesystem object
lfs_t lfs;
static ODR_t storeLittleFS(CO_storage_entry_t *entry, CO_CANmodule_t *CANmodule) {
lfs_file_t file;
CO_LOCK_OD(CANmodule);
if (lfs_file_open(&lfs, &file, entry->pathToFileOrPointerToMemory, LFS_O_RDWR | LFS_O_CREAT) != LFS_ERR_OK) {
CO_UNLOCK_OD(CANmodule);
return ODR_HW;
}
if (lfs_file_write(&lfs, &file, entry->addr, entry->len) != entry->len) {
lfs_file_close(&lfs, &file);
CO_UNLOCK_OD(CANmodule);
return ODR_HW;
}
lfs_file_close(&lfs, &file);
CO_UNLOCK_OD(CANmodule);
return ODR_OK;
}
static ODR_t restoreLittleFS(CO_storage_entry_t *entry, CO_CANmodule_t *CANmodule) {
lfs_file_t file;
CO_LOCK_OD(CANmodule);
if (lfs_file_open(&lfs, &file, entry->pathToFileOrPointerToMemory, LFS_O_RDWR) != LFS_ERR_OK) {
CO_UNLOCK_OD(CANmodule);
return ODR_HW;
}
if (lfs_file_read(&lfs, &file, entry->addr, entry->len) != entry->len) {
lfs_file_close(&lfs, &file);
CO_UNLOCK_OD(CANmodule);
return ODR_HW;
}
lfs_file_close(&lfs, &file);
CO_UNLOCK_OD(CANmodule);
return ODR_OK;
}
CO_ReturnError_t CO_storageLittleFS_init(CO_storage_t *storage,
CO_CANmodule_t *CANmodule,
OD_entry_t *OD_1010_StoreParameters,
OD_entry_t *OD_1011_RestoreDefaultParam,
CO_storage_entry_t *entries,
uint8_t entriesCount,
uint32_t *storageInitError) {
CO_ReturnError_t ret;
// Mount the filesystem
int err = lfs_mount(&lfs, &cfg);
// reformat if we can't mount the filesystem, this should only happen on the first boot
if (err) {
lfs_format(&lfs, &cfg);
lfs_mount(&lfs, &cfg);
}
ret = CO_storage_init(storage, CANmodule, OD_1010_StoreParameters,
OD_1011_RestoreDefaultParam, storeLittleFS, restoreLittleFS,
entries, entriesCount);
if (ret != CO_ERROR_NO) {
return ret;
}
*storageInitError = 0;
for (uint8_t i = 0; i < entriesCount; i++) {
CO_storage_entry_t *entry = &entries[i];
// verify arguments
if (entry->addr == NULL || entry->len == 0 || entry->subIndexOD < 2) {
*storageInitError = i;
return CO_ERROR_ILLEGAL_ARGUMENT;
}
// No action needed here if LittleFS is already mounted
}
return ret;
}
```
这个代码段尝试在 CANopenNode 存储模板的基础上实现了 LittleFS。注意这只是一个基本的实现,可能需要根据你的实际硬件和需求进行修改和优化。
## You
下面是具体的flash相关的代码:
uint16_t flash_buf[SECTOR_SIZE / 2];
/**
* @brief read data using halfword mode
* @param read_addr: the address of reading
* @param p_buffer: the buffer of reading data
* @param num_read: the number of reading data
* @retval none
*/
void flash_read(uint32_t read_addr, uint16_t *p_buffer, uint16_t num_read)
{
uint16_t i;
for(i = 0; i < num_read; i++)
{
p_buffer[i] = *(uint16_t*)(read_addr);
read_addr += 2;
}
}
/**
* @brief write data using halfword mode without checking
* @param write_addr: the address of writing
* @param p_buffer: the buffer of writing data
* @param num_write: the number of writing data
* @retval result
*/
error_status flash_write_nocheck(uint32_t write_addr, uint16_t *p_buffer, uint16_t num_write)
{
uint16_t i;
flash_status_type status = FLASH_OPERATE_DONE;
for(i = 0; i < num_write; i++)
{
status = flash_halfword_program(write_addr, p_buffer[i]);
if(status != FLASH_OPERATE_DONE)
return ERROR;
write_addr += 2;
}
return SUCCESS;
}
/**
* @brief write data using halfword mode with checking
* @param write_addr: the address of writing
* @param p_buffer: the buffer of writing data
* @param num_write: the number of writing data
* @retval result
*/
error_status flash_write(uint32_t write_addr, uint16_t *p_buffer, uint16_t num_write)
{
uint32_t offset_addr;
uint32_t sector_position;
uint16_t sector_offset;
uint16_t sector_remain;
uint16_t i;
flash_status_type status = FLASH_OPERATE_DONE;
flash_unlock();
offset_addr = write_addr - FLASH_BASE;
sector_position = offset_addr / SECTOR_SIZE;
sector_offset = (offset_addr % SECTOR_SIZE) / 2;
sector_remain = SECTOR_SIZE / 2 - sector_offset;
if(num_write <= sector_remain)
sector_remain = num_write;
while(1)
{
flash_read(sector_position * SECTOR_SIZE + FLASH_BASE, flash_buf, SECTOR_SIZE / 2);
for(i = 0; i < sector_remain; i++)
{
if(flash_buf[sector_offset + i] != 0xFFFF)
break;
}
if(i < sector_remain)
{
/* wait for operation to be completed */
status = flash_operation_wait_for(ERASE_TIMEOUT);
if((status == FLASH_PROGRAM_ERROR) || (status == FLASH_EPP_ERROR))
flash_flag_clear(FLASH_PRGMERR_FLAG | FLASH_EPPERR_FLAG);
else if(status == FLASH_OPERATE_TIMEOUT)
return ERROR;
status = flash_sector_erase(sector_position * SECTOR_SIZE + FLASH_BASE);
if(status != FLASH_OPERATE_DONE)
return ERROR;
for(i = 0; i < sector_remain; i++)
{
flash_buf[i + sector_offset] = p_buffer[i];
}
if(flash_write_nocheck(sector_position * SECTOR_SIZE + FLASH_BASE, flash_buf, SECTOR_SIZE / 2) != SUCCESS)
return ERROR;
}
else
{
if(flash_write_nocheck(write_addr, p_buffer, sector_remain) != SUCCESS)
return ERROR;
}
if(num_write == sector_remain)
break;
else
{
sector_position++;
sector_offset = 0;
p_buffer += sector_remain;
write_addr += (sector_remain * 2);
num_write -= sector_remain;
if(num_write > (SECTOR_SIZE / 2))
sector_remain = SECTOR_SIZE / 2;
else
sector_remain = num_write;
}
}
flash_lock();
return SUCCESS;
}
你能帮我把对应的block_device_read, block_device_prog, block_device_erase, block_device_sync实现好嘛?
## ChatGPT
在这种情况下,`block_device_read`,`block_device_prog`,`block_device_erase` 和 `block_device_sync` 函数可以基于您提供的 `flash_read` 和 `flash_write` 函数实现。我们需要注意的是,这些函数是基于 `uint16_t` 类型的,但是我们需要提供基于字节的操作,这就意味着我们需要对数据进行适当的转换。
此外,对于erase函数,通常情况下,我们需要擦除一个完整的flash块,然后写入新的数据。在你给出的代码中,这个操作被隐式地包含在 `flash_write` 函数中。为了兼容这种情况,我们可以在 `block_device_erase` 函数中仅执行一个dummy操作,因为实际的erase操作会在 `block_device_prog` 函数中随后的写入操作中完成。
我们假设你的 Flash 块大小为 `SECTOR_SIZE`。
以下是一个可能的实现:
```c
int block_device_read(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
// Converting size in bytes to size in uint16_t
uint16_t num_read = size / 2;
// Check for size alignment
if (size % 2 != 0) {
num_read++;
}
flash_read(block * c->block_size + off, (uint16_t *)buffer, num_read);
return 0;
}
int block_device_prog(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
// Converting size in bytes to size in uint16_t
uint16_t num_write = size / 2;
// Check for size alignment
if (size % 2 != 0) {
num_write++;
}
if (flash_write(block * c->block_size + off, (uint16_t *)buffer, num_write) != SUCCESS) {
return LFS_ERR_CORRUPT;
}
return 0;
}
int block_device_erase(const struct lfs_config *c, lfs_block_t block) {
// Here we just perform a dummy operation as actual erase will happen
// during the programming (write) operation.
// If your flash device has explicit erase function, you should call it here.
return 0;
}
int block_device_sync(const struct lfs_config *c) {
// If your flash device has a write buffer and needs to be flushed,
// you should do it here. Otherwise just perform a dummy operation.
return 0;
}
```
注意这只是一个基本的实现,可能需要根据你的实际硬件和需求进行修改和优化。
## You
你还是帮我拆开吧,按照lfs的设计,erase,prog,read,write是4个原子性的功能,不应该隐式包含在内的
## ChatGPT
我理解你的需求,所以我们将需要定义一个单独的 `block_device_erase` 函数,该函数会擦除一个完整的flash块。这样我们就需要从你提供的代码中提取出对应的擦除部分,或者你可能需要提供一个额外的函数用于擦除一个flash块。对于 `block_device_prog` 函数,我们可以直接使用你提供的 `flash_write_nocheck` 函数。
同时,请注意我们仍然需要考虑数据类型转换的问题,因为你的flash操作是基于 `uint16_t` 的,而我们需要提供基于字节的操作。
以下是更新的代码:
```c
int block_device_read(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
// Converting size in bytes to size in uint16_t
uint16_t num_read = size / 2;
// Check for size alignment
if (size % 2 != 0) {
num_read++;
}
flash_read(block * c->block_size + off, (uint16_t *)buffer, num_read);
return 0;
}
int block_device_prog(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
// Converting size in bytes to size in uint16_t
uint16_t num_write = size / 2;
// Check for size alignment
if (size % 2 != 0) {
num_write++;
}
if (flash_write_nocheck(block * c->block_size + off, (uint16_t *)buffer, num_write) != SUCCESS) {
return LFS_ERR_CORRUPT;
}
return 0;
}
int block_device_erase(const struct lfs_config *c, lfs_block_t block) {
// Here we need to erase a block in flash
// If your flash device has explicit erase function, you should call it here
// For example:
// if (flash_erase_block(block) != SUCCESS) {
// return LFS_ERR_CORRUPT;
// }
// In this case, I'll assume you have a function named "flash_erase_block"
// that takes the block number as a parameter. The block number is
// calculated as (block_address / block_size).
if (flash_erase_block(block) != SUCCESS) {
return LFS_ERR_CORRUPT;
}
return 0;
}
int block_device_sync(const struct lfs_config *c) {
// If your flash device has a write buffer and needs to be flushed,
// you should do it here. Otherwise just perform a dummy operation.
return 0;
}
```
在这个代码中,我假设你有一个名为 `flash_erase_block` 的函数,这个函数接收一个块号作为参数。块号的计算方式为(块地址 / 块大小)。如果你的flash驱动没有提供这样的函数,你可能需要实现它。
## You
// Configuration of the filesystem
const struct lfs_config cfg = {
.read = block_device_read,
.prog = block_device_prog,
.erase = block_device_erase,
.sync = block_device_sync,
.read_size = 16,
.prog_size = 16,
.block_size = 4096,
.block_count = 128,
.cache_size = 16,
.lookahead_size = 16,
.block_cycles = 500,
};
这里的参数帮我解释下,从read_size开始
## ChatGPT
当然可以,这里是`lfs_config`结构中一些字段的含义:
- read_size: 这是文件系统每次读取的最小单位,以字节为单位。这是由底层存储硬件决定的。例如,如果你的硬件以16字节为单位读取数据,那么read_size应该设置为16。
- prog_size: 这是文件系统每次编程(写入)的最小单位,以字节为单位。这也是由底层存储硬件决定的。例如,如果你的硬件以16字节为单位写入数据,那么prog_size应该设置为16。
- block_size: 这是文件系统每次擦除的最小单位,以字节为单位。这是由底层存储硬件决定的。例如,如果你的硬件以4096字节为单位擦除数据,那么block_size应该设置为4096。
- block_count: 这是文件系统的总块数。这通常是你的存储硬件的总容量除以block_size得到的。
- cache_size: 这是文件系统的缓存大小,以字节为单位。这应该是read_size或prog_size的整数倍。这个缓存是用来缓存文件系统的元数据和文件数据的,对文件系统的性能有影响。
- lookahead_size: 这是一个用于文件系统的预先读取缓冲区的大小,以字节为单位。这应该是read_size的整数倍。Lookahead buffer用于跟踪文件系统中空闲的块,这对性能有很大影响。
- block_cycles: 这是文件系统在擦除一个块之前可以写入该块的最大次数。这可以用来均衡闪存设备的损耗。如果你的设备没有写入周期的限制,你可以设置为-1。否则,对于大多数NAND和NOR闪存设备,500是一个合理的值。
## You
stm32f4系列的flash,上述对应参数是多少?
## ChatGPT
对于STM32F4系列,你可以参照以下参数:
- read_size: STM32F4的Flash可以以字节为单位读取,所以这个值可以设置为1。
- prog_size: STM32F4的Flash以字(4字节)为单位写入,所以这个值应该是4。
- block_size: 对于STM32F4,Flash的扇区大小不是固定的,比如,它可能是16K,64K或128K。你应该根据你的具体使用情况选择适当的值。
- block_count: 这个值应该等于Flash的总大小除以block_size。对于STM32F4系列,Flash的大小可以是512KB,1MB,2MB等,具体取决于你的具体型号。
- cache_size 和 lookahead_size: 这些值对于性能优化很重要,但具体的值取决于你的应用和可用的RAM。你可能需要对这些值进行一些实验,以找到最佳的平衡点。
- block_cycles: 这个值是用来控制Wear-leveling的。对于大多数NAND和NOR Flash,一个通常的值是100,000或更高。不过,如果在你的应用中,你不太关心Wear-leveling,你可以选择将它设为-1,这将关闭Wear-leveling功能。
以上只是一种可能的配置,你可能需要根据你的具体情况进行调整。
## You
这是at32的flash sdk接口的部分定义:
#define FLASH_UNLOCK_KEY1 ((uint32_t)0x45670123) /*!< flash operation unlock order key1 */
#define FLASH_UNLOCK_KEY2 ((uint32_t)0xCDEF89AB) /*!< flash operation unlock order key2 */
#define FAP_RELIEVE_KEY ((uint16_t)0x00A5) /*!< flash fap relieve key val */
#define SLIB_UNLOCK_KEY ((uint32_t)0xA35F6D24) /*!< flash slib operation unlock order key */
#define FLASH_BANK1_START_ADDR ((uint32_t)0x08000000) /*!< flash start address of bank1 */
#define FLASH_BANK1_END_ADDR ((uint32_t)0x0807FFFF) /*!< flash end address of bank1 */
#define FLASH_BANK2_START_ADDR ((uint32_t)0x08080000) /*!< flash start address of bank2 */
#define FLASH_BANK2_END_ADDR ((uint32_t)0x080FFFFF) /*!< flash end address of bank2 */
#define FLASH_SPIM_START_ADDR ((uint32_t)0x08400000) /*!< flash start address of spim */
#define FLASH_OBF_FLAG FLASH_BANK1_OBF_FLAG /*!< flash operate busy flag */
#define FLASH_ODF_FLAG FLASH_BANK1_ODF_FLAG /*!< flash operate done flag */
#define FLASH_PRGMERR_FLAG FLASH_BANK1_PRGMERR_FLAG /*!< flash program error flag */
#define FLASH_EPPERR_FLAG FLASH_BANK1_EPPERR_FLAG /*!< flash erase/program protection error flag */
#define FLASH_BANK1_OBF_FLAG ((uint32_t)0x00000001) /*!< flash bank1 operate busy flag */
#define FLASH_BANK1_ODF_FLAG ((uint32_t)0x00000020) /*!< flash bank1 operate done flag */
#define FLASH_BANK1_PRGMERR_FLAG ((uint32_t)0x00000004) /*!< flash bank1 program error flag */
#define FLASH_BANK1_EPPERR_FLAG ((uint32_t)0x00000010) /*!< flash bank1 erase/program protection error flag */
#define FLASH_BANK2_OBF_FLAG ((uint32_t)0x10000001) /*!< flash bank2 operate busy flag */
#define FLASH_BANK2_ODF_FLAG ((uint32_t)0x10000020) /*!< flash bank2 operate done flag */
#define FLASH_BANK2_PRGMERR_FLAG ((uint32_t)0x10000004) /*!< flash bank2 program error flag */
#define FLASH_BANK2_EPPERR_FLAG ((uint32_t)0x10000010) /*!< flash bank2 erase/program protection error flag */
#define FLASH_SPIM_OBF_FLAG ((uint32_t)0x20000001) /*!< flash spim operate busy flag */
#define FLASH_SPIM_ODF_FLAG ((uint32_t)0x20000020) /*!< flash spim operate done flag */
#define FLASH_SPIM_PRGMERR_FLAG ((uint32_t)0x20000004) /*!< flash spim program error flag */
#define FLASH_SPIM_EPPERR_FLAG ((uint32_t)0x20000010) /*!< flash spim erase/program protection error flag */
#define FLASH_USDERR_FLAG ((uint32_t)0x40000001) /*!< flash user system data error flag */
#define FLASH_ERR_INT FLASH_BANK1_ERR_INT /*!< flash error interrupt */
#define FLASH_ODF_INT FLASH_BANK1_ODF_INT /*!< flash operate done interrupt */
#define FLASH_BANK1_ERR_INT ((uint32_t)0x00000001) /*!< flash bank1 error interrupt */
#define FLASH_BANK1_ODF_INT ((uint32_t)0x00000002) /*!< flash bank1 operate done interrupt */
#define FLASH_BANK2_ERR_INT ((uint32_t)0x00000004) /*!< flash bank2 error interrupt */
#define FLASH_BANK2_ODF_INT ((uint32_t)0x00000008) /*!< flash bank2 operate done interrupt */
#define FLASH_SPIM_ERR_INT ((uint32_t)0x00000010) /*!< flash spim error interrupt */
#define FLASH_SPIM_ODF_INT ((uint32_t)0x00000020) /*!< flash spim operate done interrupt */
#define FLASH_SLIB_START_SECTOR ((uint32_t)0x000007FF) /*!< flash slib start sector */
#define FLASH_SLIB_DATA_START_SECTOR ((uint32_t)0x003FF800) /*!< flash slib d-bus area start sector */
#define FLASH_SLIB_END_SECTOR ((uint32_t)0xFFC00000) /*!< flash slib end sector */
#define USD_WDT_ATO_DISABLE ((uint16_t)0x0001) /*!< wdt auto start disabled */
#define USD_WDT_ATO_ENABLE ((uint16_t)0x0000) /*!< wdt auto start enabled */
#define USD_DEPSLP_NO_RST ((uint16_t)0x0002) /*!< no reset generated when entering in deepsleep */
#define USD_DEPSLP_RST ((uint16_t)0x0000) /*!< reset generated when entering in deepsleep */
#define USD_STDBY_NO_RST ((uint16_t)0x0004) /*!< no reset generated when entering in standby */
#define USD_STDBY_RST ((uint16_t)0x0000) /*!< reset generated when entering in standby */
#define FLASH_BOOT_FROM_BANK1 ((uint16_t)0x0008) /*!< boot from bank1 */
#define FLASH_BOOT_FROM_BANK2 ((uint16_t)0x0000) /*!< boot from bank 2 or bank 1,depending on the activation of the bank */
#define ERASE_TIMEOUT ((uint32_t)0x40000000) /*!< internal flash erase operation timeout */
#define PROGRAMMING_TIMEOUT ((uint32_t)0x00100000) /*!< internal flash program operation timeout */
#define SPIM_ERASE_TIMEOUT ((uint32_t)0xFFFFFFFF) /*!< spim erase operation timeout */
#define SPIM_PROGRAMMING_TIMEOUT ((uint32_t)0x00100000) /*!< spim program operation timeout */
#define OPERATION_TIMEOUT ((uint32_t)0x10000000) /*!< flash common operation timeout */
typedef enum
{
FLASH_OPERATE_BUSY = 0x00, /*!< flash status is operate busy */
FLASH_PROGRAM_ERROR = 0x01, /*!< flash status is program error */
FLASH_EPP_ERROR = 0x02, /*!< flash status is epp error */
FLASH_OPERATE_DONE = 0x03, /*!< flash status is operate done */
FLASH_OPERATE_TIMEOUT = 0x04 /*!< flash status is operate timeout */
} flash_status_type;
typedef enum
{
FLASH_SPIM_MODEL1 = 0x01, /*!< spim model 1 */
FLASH_SPIM_MODEL2 = 0x02, /*!< spim model 2 */
} flash_spim_model_type;
flag_status flash_flag_get(uint32_t flash_flag);
void flash_flag_clear(uint32_t flash_flag);
flash_status_type flash_operation_status_get(void);
flash_status_type flash_bank1_operation_status_get(void);
flash_status_type flash_bank2_operation_status_get(void);
flash_status_type flash_spim_operation_status_get(void);
flash_status_type flash_operation_wait_for(uint32_t time_out);
flash_status_type flash_bank1_operation_wait_for(uint32_t time_out);
flash_status_type flash_bank2_operation_wait_for(uint32_t time_out);
flash_status_type flash_spim_operation_wait_for(uint32_t time_out);
void flash_unlock(void);
void flash_bank1_unlock(void);
void flash_bank2_unlock(void);
void flash_spim_unlock(void);
void flash_lock(void);
void flash_bank1_lock(void);
void flash_bank2_lock(void);
void flash_spim_lock(void);
flash_status_type flash_sector_erase(uint32_t sector_address);
flash_status_type flash_internal_all_erase(void);
flash_status_type flash_bank1_erase(void);
flash_status_type flash_bank2_erase(void);
flash_status_type flash_spim_all_erase(void);
flash_status_type flash_user_system_data_erase(void);
flash_status_type flash_word_program(uint32_t address, uint32_t data);
flash_status_type flash_halfword_program(uint32_t address, uint16_t data);
flash_status_type flash_byte_program(uint32_t address, uint8_t data);
flash_status_type flash_user_system_data_program(uint32_t address, uint8_t data);
flash_status_type flash_epp_set(uint32_t *sector_bits);
void flash_epp_status_get(uint32_t *sector_bits);
flash_status_type flash_fap_enable(confirm_state new_state);
flag_status flash_fap_status_get(void);
flash_status_type flash_ssb_set(uint8_t usd_ssb);
uint8_t flash_ssb_status_get(void);
void flash_interrupt_enable(uint32_t flash_int, confirm_state new_state);
void flash_spim_model_select(flash_spim_model_type mode);
void flash_spim_encryption_range_set(uint32_t decode_address);
flash_status_type flash_slib_enable(uint32_t pwd, uint16_t start_sector, uint16_t data_start_sector, uint16_t end_sector);
error_status flash_slib_disable(uint32_t pwd);
uint32_t flash_slib_remaining_count_get(void);
flag_status flash_slib_state_get(void);
uint16_t flash_slib_start_sector_get(void);
uint16_t flash_slib_datastart_sector_get(void);
uint16_t flash_slib_end_sector_get(void);
uint32_t flash_crc_calibrate(uint32_t start_sector, uint32_t sector_cnt);
这是littlefs调用具体实现flash操作的接口模板:
int block_device_read(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
// Converting size in bytes to size in uint16_t
uint16_t num_read = size / 2;
// Check for size alignment
if (size % 2 != 0) {
num_read++;
}
flash_read(block * c->block_size + off, (uint16_t *)buffer, num_read);
return 0;
}
int block_device_prog(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
// Converting size in bytes to size in uint16_t
uint16_t num_write = size / 2;
// Check for size alignment
if (size % 2 != 0) {
num_write++;
}
if (flash_write_nocheck(block * c->block_size + off, (uint16_t *)buffer,
num_write) != SUCCESS) {
return LFS_ERR_CORRUPT;
}
return 0;
}
int block_device_erase(const struct lfs_config *c, lfs_block_t block) {
// Here we need to erase a block in flash
// If your flash device has explicit erase function, you should call it here
// For example:
// if (flash_erase_block(block) != SUCCESS) {
// return LFS_ERR_CORRUPT;
// }
// In this case, I'll assume you have a function named "flash_erase_block"
// that takes the block number as a parameter. The block number is
// calculated as (block_address / block_size).
if (flash_erase_block(block) != SUCCESS) {
return LFS_ERR_CORRUPT;
}
return 0;
}
int block_device_sync(const struct lfs_config *c) {
// If your flash device has a write buffer and needs to be flushed,
// you should do it here. Otherwise just perform a dummy operation.
return 0;
}
把上述littlefs按照sdk接口改正确
## ChatGPT
基于AT32的Flash SDK接口,你可以根据以下方式来实现对应的LittleFS驱动函数:
```c
int block_device_read(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
// 这里的flash_read应当是基于你的SDK实现的
// 注意,AT32 SDK可能没有实现字节级别的读取,需要额外处理
// 例如,可能需要读取一个字(4字节)然后取其中的某一个字节
uint32_t addr = FLASH_BANK1_START_ADDR + block * c->block_size + off;
for (size_t i = 0; i < size; i++) {
*((uint8_t*)buffer + i) = *((uint8_t*)addr + i);
}
return 0;
}
int block_device_prog(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
uint32_t addr = FLASH_BANK1_START_ADDR + block * c->block_size + off;
flash_unlock(); //解锁flash,允许写入操作
for (size_t i = 0; i < size; i++) {
if (flash_byte_program(addr + i, *((uint8_t*)buffer + i)) != FLASH_OPERATE_DONE) {
flash_lock(); // 锁定flash,防止更多的写入操作
return LFS_ERR_CORRUPT;
}
}
flash_lock(); // 锁定flash,防止更多的写入操作
return 0;
}
int block_device_erase(const struct lfs_config *c, lfs_block_t block) {
uint32_t addr = FLASH_BANK1_START_ADDR + block * c->block_size;
flash_unlock(); //解锁flash,允许擦除操作
if (flash_sector_erase(addr) != FLASH_OPERATE_DONE) {
flash_lock(); // 锁定flash,防止更多的擦除操作
return LFS_ERR_CORRUPT;
}
flash_lock(); // 锁定flash,防止更多的擦除操作
return 0;
}
int block_device_sync(const struct lfs_config *c) {
// 对于AT32的Flash,我们可能并不需要做任何同步操作,因为写入和擦除操作应该是立即生效的。
// 如果你的硬件有特别的同步需求,你可以在这里实现它。
return 0;
}
```
请注意,这个例子假设你的LittleFS文件系统位于Flash Bank1上,你可能需要根据你的硬件布局进行适当的调整。
在写入或擦除Flash之前需要先解锁,然后完成操作后需要再锁定,以防止误操作对Flash造成破坏。在调用SDK的擦除或写入函数后,需要检查返回状态,以确定操作是否成功。
以上代码只是一种可能的实现方式,根据你的硬件或SDK版本可能需要作适当的修改。此外,还需要注意
## You
上述代码中,能否使用word_program提升效率
## ChatGPT
可以的,使用 `word_program` 代替字节或者半字的写入操作可以显著提升写入效率,尤其在处理大量数据时。但是,这也需要考虑到数据对齐的问题。下面是修改后的 `block_device_prog` 函数,使用 `flash_word_program` 替代 `flash_write_nocheck`:
```c
int block_device_prog(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
// Converting size in bytes to size in uint32_t
uint16_t num_write = size / 4;
// Check for size alignment
if (size % 4 != 0) {
num_write++;
}
const uint32_t *word_buffer = (const uint32_t *)buffer;
for (uint32_t i = 0; i < num_write; i++) {
if (flash_word_program(block * c->block_size + off + i*4, word_buffer[i]) != FLASH_OPERATE_DONE) {
return LFS_ERR_CORRUPT;
}
}
return 0;
}
```
这个版本的 `block_device_prog` 将数据视为 `uint32_t` 类型,每次使用 `flash_word_program` 写入一个 `uint32_t`。如果 `size` 不是 4 的倍数,我们多写一次以确保所有数据被写入。
请注意,这种写入方式要求 `buffer` 对齐到 4 字节边界,如果不满足此条件,可能会导致未定义行为。在某些情况下,可能需要复制数据到临时缓冲区以满足对齐要求。
## You
你修改下,只用word program写4字节对齐部分,剩下的用byte program补齐
## ChatGPT
以下是修改后的 `block_device_prog` 函数,先使用 `flash_word_program` 写入4字节对齐的部分,然后使用 `flash_byte_program` 写入剩余的部分:
```c
int block_device_prog(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
const uint8_t *byte_buffer = (const uint8_t *)buffer;
uint32_t i = 0;
// Use flash_word_program for the 4-byte aligned part of the buffer
for (i = 0; i < size / 4; i++) {
if (flash_word_program(block * c->block_size + off + i*4, *((uint32_t *)(byte_buffer + i*4))) != FLASH_OPERATE_DONE) {
return LFS_ERR_CORRUPT;
}
}
// Use flash_byte_program for the remaining part of the buffer
for (i = i*4; i < size; i++) {
if (flash_byte_program(block * c->block_size + off + i, byte_buffer[i]) != FLASH_OPERATE_DONE) {
return LFS_ERR_CORRUPT;
}
}
return 0;
}
```
这个版本的 `block_device_prog` 将首先尝试按4字节批量写入数据。对于不是4字节对齐的剩余部分,将按字节写入。请注意,这个实现假设了 `flash_word_program` 和 `flash_byte_program` 都是同步操作,它们会在操作完成后返回。如果这些函数是异步的,那么这个函数需要修改以适应异步行为。
## You
上面为啥要byte_buffer = buffer?
## ChatGPT
在这个示例中,`byte_buffer` 是一个指向 `buffer` 的 `uint8_t` 类型的指针。通过这种方式,我们可以逐字节地操作 `buffer` 中的数据。
原始的 `buffer` 是一个 `void` 类型的指针,不能直接进行数学运算或者使用下标操作,因为 `void` 类型的指针没有明确的大小。在这种情况下,我们需要将其转换为具有明确大小的指针类型,以便我们能够进行下标运算或者增加偏移量。
`uint8_t` 类型的指针(即 `byte_buffer`)具有明确的大小(1字节),所以我们可以对它进行数学运算或者使用下标操作。通过 `byte_buffer[i]`,我们能够访问 `buffer` 中的第 `i` 个字节。这就是为什么我们需要 `byte_buffer = buffer` 的原因。
## You
直接对void*做类型转换不一样的嘛?
## ChatGPT
对,你是对的。将 `void*` 类型的 `buffer` 指针转换为 `uint8_t*` 类型的 `byte_buffer` 指针,实际上就是直接对 `void*` 做了类型转换。
在C语言中,`void*` 是一种特殊的指针类型,它可以指向任何类型的数据,但是不能直接进行解引用操作。要想对 `void*` 进行解引用操作,必须先将其转换为具体的类型指针。例如,在这里,我们把 `void* buffer` 转换为 `uint8_t* byte_buffer`,然后就可以通过 `byte_buffer[i]` 方式进行解引用操作了。
这样做的好处是,一方面提高了代码的可读性,另一方面,也使得我们可以利用 `uint8_t` 的特性(即每个元素占1字节),按字节处理数据。
【 在 Oriphia 的大作中提到: 】
: 都是工具,目的都是低代码,从这个角度就是同一种东西,现在的AI远未达到可以用来编程的水平,但其他方向还可以,像stable diffusion用来生产图标就挺好。
: AI真正达到可应用的水平,应该是一种像C一样编程语言,用某种规则去表达你需要的功能,而不是自由发挥和GPT对话,离实用还差至少1年吧。但未来MCU的方向肯定是AI了,但这个时间点还不是学习AI编程的时机,倒是应该研究一下stable diffusion或者audiocraft,这2个东西是成熟的。
: 国产4核A53做到20元一片的时候,MCU的时代就结束了,GPT只是推了一把。
: ...................
--
FROM 180.111.27.*