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原理



















