[英]WRITE and READ memory mapped device registers in Linux on ARM
I am trying to read and write registers on my ARM9 (SAM9X25) following those steps : http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3750.html我正在尝试按照以下步骤在我的 ARM9 (SAM9X25) 上读写寄存器: http ://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3750.html
I ended with the following code :我以以下代码结束:
#include "stdio.h"
#define PIO_WPMR_BANK_D 0xFFFFFAE4 // PIO Write Protection Mode Register Bank D
#define PIO_PUER_BANK_D 0xFFFFFA64 // PIO Pull-Up Enable Register Bank D
#define PIO_PUSR_BANK_D 0xFFFFFA68 // PIO Pull-Up Status Register Bank D
#define MASK_LED7 0xFFDFFFFF // LED7 Mask
#define DESABLE_WRITE_PROTECTION_BANK_D 0x50494F00 // Desable write protection Bank D
int main(void) {
printf("test");
unsigned int volatile * const register_PIO_WPMR_BANK_D = (unsigned int *) PIO_WPMR_BANK_D;
unsigned int volatile * const register_PIO_PUSR_BANK_D = (unsigned int *) PIO_PUSR_BANK_D;
unsigned int volatile * const port_D = (unsigned int *) PIO_PUER_BANK_D;
*register_PIO_WPMR_BANK_D = DESABLE_WRITE_PROTECTION_BANK_D;
*port_D = *register_PIO_PUSR_BANK_D & MASK_LED7;
return 0; }
I cross compiled my code in Ubuntu 16.04 like so arm-linux-gnueabi-gcc gpio.c -o gpio
我在 Ubuntu 16.04 中交叉编译了我的代码,就像
arm-linux-gnueabi-gcc gpio.c -o gpio
But I have a Segmentation Fault
just after the printf
during the execution of the program on my board.但是在我的电路板上执行程序期间,我在
printf
之后出现了一个Segmentation Fault
。
I know the addresses are right... So why do I have this error?我知道地址是正确的...那么为什么会出现此错误?
Is it the good way ?这是好方法吗?
Thank you for your help !谢谢你的帮助!
SOLUTION :解决方案:
Thank you to @vlk I could make it work !谢谢@vlk 我可以让它工作! Here is a little example for toggling a LED :
这是一个切换 LED 的小例子:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
#define _PIOD_BANK_D 0xA00
#define _PIO_OFFSET 0xFFFFF000
/* When executing this on the board :
long sz = sysconf(_SC_PAGESIZE);
printf("%ld\n\r",sz);
We have 4096.
*/
#define _MAP_SIZE 0x1000 // 4096
#define _WPMR_OFFSET 0x0E4 // PIO Write Protection Mode Register Bank D
#define _PIO_ENABLE 0x000
#define _PIO_DISABLE 0x004
#define _PIO_STATUS 0x008
#define _OUTPUT_ENABLE 0x010
#define _OUTPUT_DISABLE 0x014
#define _OUTPUT_STATUS 0x018
#define _FILTER_ENABLE 0x020
#define _FILTER_DISABLE 0x024
#define _FILTER_STATUS 0x028
#define _OUTPUT_DATA_SET 0x030
#define _OUTPUT_DATA_CLEAR 0x034
#define _OUTPUT_DATA_STATUS 0x038
#define _PIN_DATA_STATUS 0x03c
#define _MULTI_DRIVER_ENABLE 0x050
#define _MULTI_DRIVER_DISABLE 0x054
#define _MULTI_DRIVER_STATUS 0x058
#define _PULL_UP_DISABLE 0x060
#define _PULL_UP_ENABLE 0x064
#define _PULL_UP_STATUS 0x068
#define _PULL_DOWN_DISABLE 0x090
#define _PULL_DOWN_ENABLE 0x094
#define _PULL_DOWN_STATUS 0x098
#define _DISABLE_WRITE_PROTECTION 0x50494F00 // Desable write protection
#define LED_PIN 21
int main(void) {
volatile void *gpio_addr;
volatile unsigned int *gpio_enable_addr;
volatile unsigned int *gpio_output_mode_addr;
volatile unsigned int *gpio_output_set_addr;
volatile unsigned int *gpio_output_clear_addr;
volatile unsigned int *gpio_data_status_addr;
volatile unsigned int *gpio_write_protection_addr;
int fd = open("/dev/mem", O_RDWR|O_SYNC);
if (fd < 0){
fprintf(stderr, "Unable to open port\n\r");
exit(fd);
}
gpio_addr = mmap(NULL, _MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, _PIO_OFFSET);
if(gpio_addr == MAP_FAILED){
handle_error("mmap");
}
gpio_write_protection_addr = gpio_addr + _PIOD_BANK_D + _WPMR_OFFSET;
gpio_enable_addr = gpio_addr + _PIOD_BANK_D + _PIO_ENABLE;
gpio_output_mode_addr = gpio_addr + _PIOD_BANK_D + _OUTPUT_ENABLE;
gpio_output_set_addr = gpio_addr + _PIOD_BANK_D + _OUTPUT_DATA_SET;
gpio_output_clear_addr = gpio_addr + _PIOD_BANK_D + _OUTPUT_DATA_CLEAR;
gpio_data_status_addr = gpio_addr + _PIOD_BANK_D + _OUTPUT_DATA_STATUS;
*gpio_write_protection_addr = _DISABLE_WRITE_PROTECTION;
*gpio_enable_addr = 1 << LED_PIN;
*gpio_output_mode_addr = 1 << LED_PIN; // Output
// If LED
if((*gpio_data_status_addr & (1<<LED_PIN)) > 0){
*gpio_output_clear_addr = 1 << LED_PIN;
}else{
*gpio_output_set_addr = 1 << LED_PIN;
}
return 0;
}
EDIT :编辑:
Answer for the 3) in the comments.回答评论中的3) 。 You have to change the mmap and the assignations like so if you want it to work with all the offsets (ie : mmap example ):
如果您希望它与所有偏移量一起使用,则必须像这样更改 mmap 和分配(即: mmap example ):
#define _PIO_OFFSET 0xFFFFFA00 // Instead of 0xFFFFF000
#define _MAP_SIZE 0x1000 // 4096
#define _MAP_MASK (_MAP_SIZE - 1)
#define _PA_OFFSET _PIO_OFFSET & ~_MAP_MASK
And the mmap :和 mmap :
gpio_addr = mmap(NULL, _MAP_SIZE + _PIO_OFFSET - _PA_OFFSET, PROT_READ | PROT_WRITE, MAP_SHARED, fd, _PA_OFFSET);
And for the assignation :对于分配:
gpio_enable_addr = gpio_addr + _PIO_OFFSET - (_PA_OFFSET) + _PIO_ENABLE;
You can't access registers directly, because Linux use MMU and this create for your application virtual address space which is different than physical MCU address space and access outside this virtual address space cause segmentation fault.您不能直接访问寄存器,因为 Linux 使用 MMU 并且这为您的应用程序创建了虚拟地址空间,这与物理 MCU 地址空间不同,并且在此虚拟地址空间之外访问会导致分段错误。
Only Way to access these registers in Linux (if you don't want to write kernel drivers) is to open file /dev/mem as file and map it with mmap在 Linux 中访问这些寄存器的唯一方法(如果您不想编写内核驱动程序)是打开文件 /dev/mem 作为文件并使用mmap映射它
For example I have small python library for access GPIO registers on Atmel SAM MCU gpiosam .例如,我有用于访问 Atmel SAM MCU gpiosam上的 GPIO 寄存器的小型 python 库。 You can inspire and port it to C.
您可以激发灵感并将其移植到 C。
busybox devmem
busybox devmem
is a tiny CLI utility that mmaps /dev/mem
. busybox devmem
是一个微型 CLI 实用程序,它可以映射/dev/mem
。
You can get it in Ubuntu with: sudo apt-get install busybox你可以在 Ubuntu 中使用: sudo apt-get install busybox
Usage: read 4 bytes from the physical address 0x12345678
:用法:从物理地址
0x12345678
读取 4 个字节:
sudo busybox devmem 0x12345678
Write 0x9abcdef0
to that address:将
0x9abcdef0
写入该地址:
sudo busybox devmem 0x12345678 w 0x9abcdef0
See this for a few tips on how to test it out: Accessing physical address from user space有关如何进行测试的一些提示,请参阅此内容: 从用户空间访问物理地址
Also mentioned at: https://unix.stackexchange.com/questions/4948/shell-command-to-read-device-registers还提到: https : //unix.stackexchange.com/questions/4948/shell-command-to-read-device-registers
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.