Driver: Flash Shell¶
1 功能概述¶
本文主要介绍PAN1080 EVB板flash shell演示,可以通过类shell命令来实现flash api的相关操作。
2 环境要求¶
board: pan1080a_afld_evb
uart(波特率921600) : 显示串口shell
3 编译和烧录¶
项目位置:zephyr\samples_panchip\drivers\flash_shell
统一的配置、编译、下载工具正在开发中,当前可以使用脚本进行编译和下载。
脚本位置:quick_build_samples\drivers\flash_shell.bat
。
打开脚本后默认会编译项目,编译完成时,可输入字符进行后续下载等操作:
Input the keyword to continue:
'b' build 编译项目
'r' make clean and rebuild 重新编译项目
'f' flash download 下载
'e' erase chip 擦除芯片
'o' open project by VS Code 打开 `VS Code`,可查看源码,执行编译下载等
others exit 退出
wait input:
4 演示说明¶
通过串口命令输入flash_cmd命令来实现flash api操作,通过连续两个tab
会自动补全命令,例如输入flash_cmd
后,则shell会输出flash_cmd相关细指令。
详情参考5.1 flash_shell支持的命令
,5.2 flash_cmd使用以及命令解析
.
5 开发说明¶
5.1 flash_shell支持的命令¶
flash_cmd - Flash related commands.
Subcommands:
erase :<off> <len>
Erase <len> bytes from device offset <off>, subject to
hardware page limitations.
page_count :Print the number of pages on the flash device.
page_erase :<page> [num]
Erase [num] pages (default 1), starting at page <page>.
page_layout :[start_page] [end_page]
Print layout of flash pages in the range [start_page,
end_page], which is inclusive. By default, all pages are
printed.
page_read :<page> <len> OR <page> <off> <len>
Read <len> bytes from given page, starting at page offset
<off>,or offset 0 if not given. No checks are made that
bytes read are all within the page.
page_write :<page> <off> <byte1> [... byteN]
Write given bytes to given page, starting at page offset
<off>. No checks are made that the bytes all fall within
the page. Pages must be erased before they can be written.
read :<off> <len>
Read <len> bytes from device offset <off>.
set_device :<device_name>
Set flash device by name. If a flash device was not found,
this command must be run first to bind a device to this
module.
write :<off> <byte1> [... byteN]
Write given bytes, starting at device offset <off>.
Pages must be erased before they can be written.
write_block_size :Print the device's write block size. This is the smallest
amount of data which may be written to the device, in
bytes.
write_unaligned :<off> <byte1> [... byteN]
Write given bytes, starting at device offset <off>.
Being unaligned, affected memory areas are backed up,
erased, protected and then overwritten.
This command is designed to test writing to large flash
pages.
write_pattern :<off> <len>
Writes a pattern of (0x00 0x01 .. 0xFF 0x00 ..) of
length<len> at the offset <off>.
Unaligned writing is used, i.e. protection and erasing are
automated.This command is designed to test writing to large
flash pages.
5.2 flash_cmd使用以及命令解析¶
5.2.1 使用flash_cmd read
读取地址0x1000处16个字节¶
uart:~$ flash_cmd read 0x1000 0x10
53 46 1d 68 | af 68 fb 68 |00 2b 04 d0 | 00 21 28 00
5.2.2 使用flash_cmd write
写入地址0xc8000处4个字节¶
uart:~$ flash_cmd read 0xc8000 0x4 //从800K地址处读取4个字节,因为该地址正常没写入过,默认为全ff
ff ff ff ff
uart:~$ flash_cmd write 0xc8000 0x1 0x2 0x3 0x4 //从800K地址处写入4个字节:0x1 0x2 0x3 0x4
Reading back written bytes:
01 02 03 04
uart:~$ flash_cmd read 0xc8000 0x4 //从800K地址处读取4个字节:0x1 0x2 0x3 0x4
01 02 03 04
5.2.3 使用flash_cmd erase
擦除地址0xc8000处4K字节¶
uart:~$ flash_cmd erase 0xc8000 0x1000 //从800K地址处擦除4K字节,默认擦写必须4K对齐,这个可以使用flash_cmd write_block_size
//获得,该值默认在dts中定义。
uart:~$ flash_cmd read 0xc8000 0x4
ff ff ff ff
5.2.4 flash_cmd read
命令代码解析¶
flash_cmd所有的命令均在下面,从SHELL_CMD_ARG(read, NULL, READ_HELP, cmd_read, 3, 0)
我们可以知道read命令对应的实现函数,在cmd_read中。
SHELL_STATIC_SUBCMD_SET_CREATE(sub_flash,
/* Alphabetically sorted to ensure correct Tab autocompletion. */
SHELL_CMD_ARG(erase, NULL, ERASE_HELP, cmd_erase, 3, 0),
#ifdef CONFIG_FLASH_PAGE_LAYOUT
SHELL_CMD_ARG(page_count, NULL, PAGE_COUNT_HELP, cmd_page_count, 1, 0),
SHELL_CMD_ARG(page_erase, NULL, PAGE_ERASE_HELP, cmd_page_erase, 2, 1),
SHELL_CMD_ARG(page_layout, NULL, PAGE_LAYOUT_HELP,
cmd_page_layout, 1, 2),
SHELL_CMD_ARG(page_read, NULL, PAGE_READ_HELP, cmd_page_read, 3, 1),
SHELL_CMD_ARG(page_write, NULL, PAGE_WRITE_HELP,
cmd_page_write, 3, 255),
#endif
SHELL_CMD_ARG(read, NULL, READ_HELP, cmd_read, 3, 0),
SHELL_CMD_ARG(set_device, NULL, SET_DEV_HELP, cmd_set_dev, 2, 0),
SHELL_CMD_ARG(write, NULL, WRITE_HELP, cmd_write, 3, 255),
SHELL_CMD_ARG(write_block_size, NULL, WRITE_BLOCK_SIZE_HELP,
cmd_write_block_size, 1, 0),
SHELL_CMD_ARG(write_unaligned, NULL, WRITE_UNALIGNED_HELP,
cmd_write_unaligned, 3, 255),
SHELL_CMD_ARG(write_pattern, NULL, WRITE_PATTERN_HELP,
cmd_write_pattern, 3, 255),
SHELL_SUBCMD_SET_END /* Array terminated. */
);
SHELL_CMD_REGISTER(flash_cmd, &sub_flash, "Flash related commands.", NULL);
5.3 Flash API使用参考¶
5.3.1 flash_read API参考¶
cmd_read也是调用do_read函数,对于flash_read函数我们可以参考do_read函数:
/* Read bytes, dumping contents to console and printing on error. */
static int do_read(const struct shell *shell, off_t offset, size_t len)
{
uint8_t buf[64];
int ret;
while (len > sizeof(buf)) {
ret = flash_read(flash_device, offset, buf, sizeof(buf));
if (ret) {
goto err_read;
}
dump_buffer(shell, buf, sizeof(buf));
len -= sizeof(buf);
offset += sizeof(buf);
}
ret = flash_read(flash_device, offset, buf, len);
if (ret) {
goto err_read;
}
dump_buffer(shell, buf, len);
return 0;
err_read:
PR_ERROR(shell, "flash_read error: %d\n", ret);
return ret;
}
5.3.2 flash_write API参考¶
/* Write bytes and printing on error. */
static int do_write(const struct shell *shell, off_t offset, uint8_t *buf,
size_t len, bool read_back)
{
int ret;
ret = flash_write(flash_device, offset, buf, len);
if (ret) {
PR_ERROR(shell, "flash_write failed (err:%d).\n", ret);
return ret;
}
if (read_back) {
PR_SHELL(shell, "Reading back written bytes:\n");
ret = do_read(shell, offset, len);
}
return ret;
}
5.3.3 flash_erase API参考¶
/* Erase area and printing on error. */
static int do_erase(const struct shell *shell, off_t offset, size_t size)
{
int ret;
ret = flash_erase(flash_device, offset, size);
if (ret) {
PR_ERROR(shell, "flash_erase failed (err:%d).\n", ret);
return ret;
}
return ret;
}
5.3.4 do_write_unaligned 非对齐地址写参考实现¶
static int do_write_unaligned(const struct shell *shell, off_t offset, uint8_t *buf,
size_t len, bool read_back)
{
int ret = 0;
size_t page_size = flash_get_write_block_size(flash_device);
size_t size_before = offset % page_size;
size_t size_after = page_size - ((size_before + len) % page_size);
size_t aligned_size = size_before + len + size_after;
off_t start_page = offset - size_before;
off_t last_page = start_page + aligned_size - page_size;
bool single_page_write = (size_before + len < page_size);
char *before_data;
char *after_data;
if (0 == size_before && 0 == size_after) {
/* Aligned write */
flash_erase(flash_device, offset, len);
flash_write(flash_device, offset, buf, len);
return 0;
}
before_data = k_malloc(page_size);
after_data = k_malloc(page_size);
if (!before_data || !after_data) {
PR_ERROR(shell, "No heap memory for flash manipulation\n");
ret = -ENOMEM;
goto free_buffers;
}
/* Stash useful data from the pages that will be affected. */
if (single_page_write) {
/* Read old data before new data is written. */
if (size_before) {
flash_read(flash_device, start_page, before_data, size_before);
}
/* Fill the with new data. */
memcpy(before_data + size_before, buf, len);
/* Fill the last part of old data. */
if (size_after) {
flash_read(flash_device, offset + len,
before_data + size_before + len,
size_after);
}
} else {
/* Multipage write, different start and end pages. */
if (size_before) {
flash_read(flash_device, start_page, before_data,
size_before);
/* Fill the rest with new data. */
memcpy(before_data + size_before, buf,
page_size - size_before);
}
if (size_after) {
/* Copy ending part of new data. */
memcpy((void *)after_data,
(void *)(buf + len -
((len + size_before) % page_size)),
page_size - size_after);
/* Copy ending part of flash page. */
flash_read(flash_device, offset + len,
after_data + (page_size - size_after),
size_after);
}
}
/* Erase all the pages that overlap with new data. */
flash_erase(flash_device, start_page, aligned_size);
/* Write stashed and new data. */
if (single_page_write || size_before > 0) {
/* Write first page if available. */
flash_write(flash_device, start_page, before_data,
page_size);
}
if (!single_page_write) {
size_t middle_data_len = aligned_size;
off_t middle_page_start = start_page;
off_t data_offset = (off_t)buf;
/* Write the middle bit if available */
if (size_before > 0) {
middle_page_start += page_size;
middle_data_len -= page_size;
data_offset += (page_size - size_before);
}
if (size_after > 0) {
middle_data_len -= page_size;
}
if (middle_data_len > 0) {
flash_write(flash_device, middle_page_start,
(const void *)data_offset,
middle_data_len);
}
/* Write the last page if needed. */
if (size_after > 0) {
flash_write(flash_device, last_page, after_data,
page_size);
}
}
if (read_back) {
PR_SHELL(shell, "Reading back written bytes:\n");
ret = do_read(shell, offset, len);
}
free_buffers:
k_free(before_data);
k_free(after_data);
return ret;
}