ARM 上 Linux 中的 WRITE 和 READ 内存映射设备寄存器 [英] WRITE and READ memory mapped device registers in Linux on ARM

查看:29
本文介绍了ARM 上 Linux 中的 WRITE 和 READ 内存映射设备寄存器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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


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


I cross compiled my code in Ubuntu 16.04 like so 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.
I know the addresses are right... So why do I have this error?
Is it the good way ?
Thank you for your help !

解决方案:
谢谢@vlk,我可以让它工作!这是一个切换 LED 的小例子:

SOLUTION :
Thank you to @vlk I could make it work ! Here is a little example for toggling a 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

",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

");
        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 和分配(即:mmap 示例):

EDIT :
Answer for the 3) in the comments. You have to change the mmap and the assignations like so if you want it to work with all the offsets (i.e : 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

还有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 地址空间的虚拟地址空间,并在此之外访问虚拟地址空间导致分段错误.

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 中访问这些寄存器的唯一方法(如果您不想编写内核驱动程序)是将文件/dev/mem 作为文件打开并使用 mmap

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

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

For example I have small python library for access GPIO registers on Atmel SAM MCU gpiosam. You can inspire and port it to C.

这篇关于ARM 上 Linux 中的 WRITE 和 READ 内存映射设备寄存器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆