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

标签 c linux arm cpu-registers gpio

我正尝试按照以下步骤在我的 ARM9 (SAM9X25) 上读取和写入寄存器:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3750.html
我以以下代码结束:

#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; }


我在 Ubuntu 16.04 中像这样交叉编译了我的代码 arm-linux-gnueabi-gcc gpio.c -o gpio
但是我有一个 Segmentation Fault 在我的程序执行过程中 printf 之后。
我知道地址是正确的...那么为什么我会出现这个错误?
这是好的方法吗?
谢谢你的帮助!

解决方案:
感谢@vlk 我可以让它工作!这是一个切换 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;
}

编辑:
回答评论中的3)。如果您希望它使用所有偏移量(即:mmap example),您必须像这样更改 mmap 和分配:

#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

还有 mmap :

gpio_addr = mmap(NULL, _MAP_SIZE + _PIO_OFFSET - _PA_OFFSET, PROT_READ | PROT_WRITE, MAP_SHARED, fd, _PA_OFFSET);

对于作业:

gpio_enable_addr = gpio_addr + _PIO_OFFSET - (_PA_OFFSET) + _PIO_ENABLE;

最佳答案

您不能直接访问寄存器,因为 Linux 使用 MMU,这会为您的应用程序创建不同于物理 MCU 地址空间的虚拟地址空间,并且访问该虚拟地址空间之外会导致段错误。

在 Linux 中访问这些寄存器的唯一方法(如果您不想编写内核驱动程序)是将文件/dev/mem 作为文件打开并将其映射到 mmap

例如,我有一个小型 python 库,用于访问 Atmel SAM MCU 上的 GPIO 寄存器 gpiosam .您可以启发并将其移植到 C。

关于c - 在 ARM 上的 Linux 中写入和读取内存映射设备寄存器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44312599/

相关文章:

c++ - 查找定义我的 C/C++ 函数/宏的文件的方法比 'grep' 更简单

linux - 如何仅使用文件名而不带PID的kill命令

linux - Bash:从文本文件 VAR=VALUE 格式读取变量

android - 在没有内存访问的线路上发出 SIGBUS 信号

stack - 谁来选择ARM堆栈方向?

c++ - 如何查找和比较字符数组的元素

c - 安排 Windows 在 C 上启动

floating-point - x86_64 和 ARMv8.2-A 之间不同的浮点计算结果

c - 在 gcc 中禁用可变长度自动数组

linux - Tomcat 未在 Linux 中启动 --- 运行 startup.sh 的权限被拒绝