devmem2读写内存
- 自定义msh命令devmem2
- 验证msh命令devmem2
- 读CPUID
- 读写全局变量
devmem2模块可实现对设备寄存器的读写操作。在RT-Thread的命令行组件Fish中添加devmem2模块,用户可在终端输入devmem2相关命令,FinSH根据输入对指定寄存器进行读写,并将结果显示到控制终端。
关于FinSH详细内容见:Finsh
自定义msh命令devmem2
msh是FinSH的传统命令行模式,因其模式体积小,使用方便,能够解决C-style(C语言解释器模式)的弊端等,被广泛引用。
自定义的 msh 命令,可以在 msh 模式下被运行,将一个命令导出到 msh 模式可以使用如下宏接口:
MSH_CMD_EXPORT(name, desc);
name:要导出的命令,desc:导出命令的描述
在ENV工具中配置MSH:command shell,如下图:
添加devmem2模块至FinSH组件,需要在工程中的FinSH路径下cmd.c(如:…/bsp/raspberry-pico/dist/project/rt-thread/components/finsh/cmd.c)添加devmem2相关源码。由于pico开发板没有mmp内存映射模块,所以读写寄存器是针对物理地址进行操作。其核心代码:
void devmem2(int argc, char **argv) {
void *phy_addr;
unsigned long read_result, writeval;
off_t target;
int access_type = 'w';
if(argc < 2) {
fprintf(stderr, "\nUsage:\t%s { address } [ type [ data ] ]\n"
"\taddress : memory address to act upon\n"
"\ttype : access operation type : [b]yte, [h]alfword, [w]ord\n"
"\tdata : data to be written\n\n",
argv[0]);
return;
}
target = strtoul(argv[1], 0, 0);
if(argc > 2)
access_type = tolower(argv[2][0]);
phy_addr = (void*)target;
printf("phy_addr:%x\n",phy_addr);
switch(access_type) {
case 'b':
read_result = *((unsigned char *) phy_addr);
break;
case 'h':
read_result = *((unsigned short *) phy_addr);
break;
case 'w':
read_result = *((unsigned long *) phy_addr);
break;
default:
fprintf(stderr, "Illegal data type '%c'.\n", access_type);
return;
}
printf("Value at address 0x%X (%p): 0x%X\n", target, phy_addr, read_result);
if(argc > 3) {
writeval = strtoul(argv[3], 0, 0);
switch(access_type) {
case 'b':
*((unsigned char *) phy_addr) = writeval;
read_result = *((unsigned char *) phy_addr);
break;
case 'h':
*((unsigned short *) phy_addr) = writeval;
read_result = *((unsigned short *) phy_addr);
break;
case 'w':
*((unsigned long *) phy_addr) = writeval;
read_result = *((unsigned long *) phy_addr);
break;
}
printf("Written 0x%X; readback 0x%X\n", writeval, read_result);
}
}
MSH_CMD_EXPORT(devmem2, devmem2 sample: devmem2 { address } [ type [ data ] ]);
添加devmem会引入新的头文件,在scons构建项目时,会出现因找不到个别头文件或源文件而报错,针对此问题,需要在当前工程的根目录下,查找缺失的头文件,并将头文件所在的路径添加至project/libraries/SConscript中,如:
验证msh命令devmem2
读CPUID
添加devmem2模块并成功构建工程后,将在msh命令行按下Tab健查看devmem2命令。如图:
读取设备的CPUID,查看pico rp2040说明文档,获取其ARM寄存器的其实地址为0xe0000000,其CPUID的偏移地址offset为0xed00,故其CPUID的地址为0xe000ed00。通过CPUID寄存器的值为0x410cc601与devmem2查看的一致。如图:
由于pico中地址范围为0xd0000000-0xefffffff仅支持word size,故halfword及byte的type读操作无效,如图:
此处,读某一寄存器,验证其type为w、h、b效果,如图:
读写全局变量
在…/projectt/applications/main.c中添加一全局变量test_value,scons构建项目,通过…/project/rt-thread.map查看test_value变量的地址为0x200012c4,如图:
/*
main.c
*/
#include <rtthread.h>
#include <rtdevice.h>
#define LED_PIN 25
int test_value;
int main(void)
{
rt_kprintf("Hello, RT-Thread!\n");
test_value = 0;
rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT);
while (1)
{
rt_pin_write(LED_PIN, 1);
rt_thread_mdelay(3000);
rt_pin_write(LED_PIN, 0);
rt_thread_mdelay(1000);
}
}
通过devmem2命令读写test_value对应寄存器内的值,如图:
关于逻辑地址(虚拟地址)与物理地址(设备的实际RAM地址/硬盘空间)及映射之间说明可参考如下:
操作系统中逻辑地址和物理地址的区别
linux内存映射mmap原理分析
内存映射原理和内核是如何实现的,完全分析mmap原理