简体   繁体   English

在 ARM 上的 Linux 中写入和读取内存映射设备寄存器

[英]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.

相关问题 控制C中存储器映射寄存器的读写访问宽度 - Controlling read and write access width to memory mapped registers in C 如何使用关键字volatile读写内存映射的寄存器? - How to read and write memory mapped registers using keyword volatile? 与linux中的内存映射设备通信 - communicating with a memory mapped device in linux ARM linux用户空间gpio操作使用mmap / dev / mem方法(能够写入GPIO寄存器,但无法读取它们) - ARM linux userspace gpio operations using mmap /dev/mem approach (able to write to GPIO registers, but fail to read from them) 只读内存映射寄存器在C中用`volatile const`定义,但在C ++中只用`volatile`定义 - Read-only memory-mapped registers defined with `volatile const` in C but only `volatile` in C++ Linux-内存映射文件 - Linux - Memory Mapped File 在arm-linux中使用ptrace获取寄存器 - Get registers with ptrace in arm-linux 映射的文件在读取时不会占用内存,但在写入时会占用内存 - Mapped file doesn't take memory on read, but does on write 在ioctl linux设备驱动程序中读写 - Read and write in ioctl linux device driver 用 C 对 linux 串口设备进行初始化、读写 - init, read and write for linux serial device with C
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM