2012-10-29 11 views
28

के माध्यम से मैं बीगलबोन पर एक एलईडी झपकी के लिए एक सी प्रोग्राम लिखने की कोशिश कर रहा हूं। मुझे पता है कि मैं sysfs के तरीके का उपयोग कर सकता हूं ... लेकिन मैं देखना चाहता हूं कि भौतिक पता स्थान मैपिंग/dev/mem के साथ एक ही परिणाम प्राप्त करना संभव है या नहीं।ड्राइविंग बीगलबोन जीपीआईओ/dev/mem

मैं निम्नलिखित सामग्री बुद्धि beaglebone_gpio.h एक हेडर फाइल है,:

#ifndef _BEAGLEBONE_GPIO_H_ 
#define _BEAGLEBONE_GPIO_H_ 

#define GPIO1_START_ADDR 0x4804C000 
#define GPIO1_END_ADDR 0x4804DFFF 
#define GPIO1_SIZE (GPIO1_END_ADDR - GPIO1_START_ADDR) 
#define GPIO_OE 0x134 
#define GPIO_SETDATAOUT 0x194 
#define GPIO_CLEARDATAOUT 0x190 

#define USR0_LED (1<<21) 
#define USR1_LED (1<<22) 
#define USR2_LED (1<<23) 
#define USR3_LED (1<<24) 

#endif 

और उसके बाद मैं अपने सी कार्यक्रम, gpiotest.c

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/mman.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include "beaglebone_gpio.h" 

int main(int argc, char *argv[]) { 
    volatile void *gpio_addr = NULL; 
    volatile unsigned int *gpio_oe_addr = NULL; 
    volatile unsigned int *gpio_setdataout_addr = NULL; 
    volatile unsigned int *gpio_cleardataout_addr = NULL; 
    unsigned int reg; 
    int fd = open("/dev/mem", O_RDWR); 

    printf("Mapping %X - %X (size: %X)\n", GPIO1_START_ADDR, GPIO1_END_ADDR, GPIO1_SIZE); 

    gpio_addr = mmap(0, GPIO1_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO1_START_ADDR); 

    gpio_oe_addr = gpio_addr + GPIO_OE; 
    gpio_setdataout_addr = gpio_addr + GPIO_SETDATAOUT; 
    gpio_cleardataout_addr = gpio_addr + GPIO_CLEARDATAOUT; 

    if(gpio_addr == MAP_FAILED) { 
     printf("Unable to map GPIO\n"); 
     exit(1); 
    } 
    printf("GPIO mapped to %p\n", gpio_addr); 
    printf("GPIO OE mapped to %p\n", gpio_oe_addr); 
    printf("GPIO SETDATAOUTADDR mapped to %p\n", gpio_setdataout_addr); 
    printf("GPIO CLEARDATAOUT mapped to %p\n", gpio_cleardataout_addr); 

    reg = *gpio_oe_addr; 
    printf("GPIO1 configuration: %X\n", reg); 
    reg = reg & (0xFFFFFFFF - USR1_LED); 
    *gpio_oe_addr = reg; 
    printf("GPIO1 configuration: %X\n", reg); 

    printf("Start blinking LED USR1\n"); 
    while(1) { 
     printf("ON\n"); 
     *gpio_setdataout_addr= USR1_LED; 
     sleep(1); 
     printf("OFF\n"); 
     *gpio_cleardataout_addr = USR1_LED; 
     sleep(1); 
    } 

    close(fd); 
    return 0; 
} 

है उत्पादन होता है:

Mapping 4804C000 - 4804DFFF (size: 1FFF) 
GPIO mapped to 0x40225000 
GPIO OE mapped to 40225134 
GPIO SEDATAOUTADDR mapped to 0x40225194 
GPIO CLEARDATAOUTADDR mapped to 0x40225190 
GPIO1 configuration: FE1FFFFF 
GPIO1 configuratino: FE1FFFFF 
Start blinking LED USR1 
ON 
OFF 
ON 
OFF 
... 

लेकिन मुझे एलईडी ब्लिंकिंग दिखाई नहीं दे रही है।

आप इस कार्यक्रम के उत्पादन कॉन्फ़िगरेशन सही है, FE1FFFFF, सुसंगत है से देख सकते हैं के बाद से GPIO1_21, GPIO1_22, GPIO1_23 और GPIO1_24 आउटपुट के रूप में विन्यस्त कर रहे हैं, हर एक एक एलईडी चला।

कारण के बारे में कोई विचार?

+1

मैं समाधान पाया है ... यह केवल MAP_PRIVATE के बजाय mmap में MAP_SHARED उपयोग करने के लिए आवश्यक है। मैं वैसे भी सवाल छोड़ देता हूँ। शायद यह किसी और के लिए उपयोगी होगा। – salvo

+0

यह आपके अपने प्रश्न का उत्तर देने के लिए बिल्कुल सही अभ्यास है, जब तक कि दूसरों को इसका उत्तर देने का एक उचित मौका भी दिया जाता है। – Lundin

उत्तर

8

ठीक है:

pio_addr = mmap(0, GPIO1_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO1_START_ADDR); 
+1

क्या आप सुनिश्चित हैं कि यह ठीक है, मैंने सोचा था कि आपको MAP_PRIVATE के बजाय mmap में MAP_SHARED का उपयोग करने की आवश्यकता है। –

+0

आप सही हैं ... धन्यवाद – salvo

+0

क्या आप समझा सकते हैं कि MAP_SHARED की आवश्यकता क्यों है? – RicoRico

12

सावधान रहें। यह पहली बार ब्लश पर काम करता है, लेकिन यह सीधे एक रजिस्टर को लिखता है कि जीपीआईओ नियंत्रक चालक का मानना ​​है कि इसका स्वामित्व है। इससे इस जीपीआईओ लाइन पर या एक ही बैंक में जीपीआईओ पर साइड इफेक्ट्स को ट्रैक करने के लिए अजीब और कड़ी मेहनत होगी। इसके लिए विश्वसनीय रूप से काम करने के लिए आपको कर्नेल जीपीआईओ ड्राइवर से पूरे बैंक को अक्षम करने की आवश्यकता है।

+0

यह सही है ... धन्यवाद। – salvo

+1

क्या GPIO नियंत्रक ड्राइवर को अक्षम करने का कोई तरीका है ताकि यह कोई मुद्दा न हो? – sheridp

8

मूल पोस्ट में दिखाया गया कोड नवीनतम बीगलबोन ब्लैक और इसके संबंधित 3.12 कर्नेल के साथ काम नहीं करता है। नियंत्रण रजिस्टर ऑफसेट बदल गया प्रतीत होता है; निम्नलिखित कोड को ठीक से काम करने के लिए सत्यापित किया गया है:

#define GPIO0_BASE 0x44E07000 
#define GPIO1_BASE 0x4804C000 
#define GPIO2_BASE 0x481AC000 
#define GPIO3_BASE 0x481AE000 

#define GPIO_SIZE 0x00000FFF 

// OE: 0 is output, 1 is input 
#define GPIO_OE 0x14d 
#define GPIO_IN 0x14e 
#define GPIO_OUT 0x14f 

#define USR0_LED (1<<21) 
#define USR1_LED (1<<22) 
#define USR2_LED (1<<23) 
#define USR3_LED (1<<24) 

int mem_fd; 
char *gpio_mem, *gpio_map; 

// I/O access 
volatile unsigned *gpio; 

static void io_setup(void) 
{ 
    // Enable all GPIO banks 
    // Without this, access to deactivated banks (i.e. those with no clock source set up) will (logically) fail with SIGBUS 
    // Idea taken from https://groups.google.com/forum/#!msg/beagleboard/OYFp4EXawiI/Mq6s3sg14HoJ 
    system("echo 5 > /sys/class/gpio/export"); 
    system("echo 65 > /sys/class/gpio/export"); 
    system("echo 105 > /sys/class/gpio/export"); 

    /* open /dev/mem */ 
    if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC)) < 0) { 
      printf("can't open /dev/mem \n"); 
      exit (-1); 
    } 

    /* mmap GPIO */ 
    gpio_map = (char *)mmap(
      0, 
      GPIO_SIZE, 
      PROT_READ|PROT_WRITE, 
      MAP_SHARED, 
      mem_fd, 
      GPIO1_BASE 
    ); 

    if (gpio_map == MAP_FAILED) { 
      printf("mmap error %d\n", (int)gpio_map); 
      exit (-1); 
    } 

    // Always use the volatile pointer! 
    gpio = (volatile unsigned *)gpio_map; 

    // Get direction control register contents 
    unsigned int creg = *(gpio + GPIO_OE); 

    // Set outputs 
    creg = creg & (~USR0_LED); 
    creg = creg & (~USR1_LED); 
    creg = creg & (~USR2_LED); 
    creg = creg & (~USR3_LED); 

    // Set new direction control register contents 
    *(gpio + GPIO_OE) = creg; 
} 

int main(int argc, char **argv) 
{ 
    io_setup(); 
    while (1) { 
     // Set LEDs 
     *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) | USR0_LED; 
     *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) | USR1_LED; 
     *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) | USR2_LED; 
     *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) | USR3_LED; 

     sleep(1); 

     // Clear LEDs 
     *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) & (~USR0_LED); 
     *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) & (~USR1_LED); 
     *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) & (~USR2_LED); 
     *(gpio + GPIO_OUT) = *(gpio + GPIO_OUT) & (~USR3_LED); 

     sleep(1); 
    } 

    return 0; 
} 

मैं इस पोस्ट के रूप में यह प्रतीत होता है कि mmap एड पहुँच 3.8 कर्नेल के आसपास काम कर बंद कर दिया, और कोई भी एक काम कर समाधान तब से पोस्ट किया गया है। मुझे/sys/class/gpio इंटरफेस का उपयोग कर नियंत्रण रजिस्टर ऑफ़सेट को रिवर्स-इंजीनियर करना था; मुझे आशा है कि यह जवाब नए कर्नेल के साथ बीगलबोन जीपीआईओ का उपयोग करने से जुड़ी कुछ निराशा को कम कर देगा।

कोड बीएसडी लाइसेंस के तहत लाइसेंस प्राप्त है - इसे कहीं भी उपयोग करने में संकोच न करें।

संपादित करें: उपयोगकर्ता 3078565 उपरोक्त उनके उत्तर में सही है। आपको डिफॉल्ट उपयोगकर्ता एलईडी जीपीआईओ ड्राइवरों को या तो अपने ट्रिगर्स को किसी भी डिवाइस पर सेट करके या डिवाइस पेड़ को संपादित करके कर्नेल से पूरी तरह छुपाकर अक्षम करने की आवश्यकता होगी। ऐसा करने में विफलता के परिणामस्वरूप एल ई डी चमकती जा रही है, लेकिन कभी-कभी कर्नेल जीपीआईओ ड्राइवर द्वारा अपने राज्यों को ओवरराइड किया जाता है।

यह मेरे मूल एप्लिकेशन के लिए कोई मुद्दा नहीं था क्योंकि यह जीपीआईओ बैंक 0 का उपयोग करता है, जिसे बड़े पैमाने पर कर्नेल जीपीआईओ ड्राइवरों द्वारा अनदेखा किया जाता है।

+0

हाय, थैक्स आपके कोड के लिए। बस सोच रहा है कि आपने इसे बंद करें जैसे 'बंद करें (mem_fd); '। एक नौसिखिया के रूप में, मैं सिर्फ यह जानना चाहता हूं कि इसे बंद करना आवश्यक है या नहीं? –

+0

ऑफ़सेट पता त्रुटि। इसे 0x14d, 0x4e, 0x4f के रूप में सही करें 0x14d, 0x14e, 0x14f –

+0

एक्सेस बैंकों से पहले निर्यात पर टिप के लिए धन्यवाद, डीबग करना मुश्किल है। – DXM

4

आपको उपयोगकर्ता के स्थान पर नियंत्रण करने की कोशिश कर रहे हार्डवेयर के किसी भी टुकड़े के लिए घड़ी को सक्षम करने की आवश्यकता हो सकती है। सौभाग्य से, आप हार्डवेयर के अपने विशेष टुकड़े के लिए घड़ी नियंत्रण रजिस्टर के साथ dev/mem और mmap() का उपयोग कर सकते हैं, जैसे कि कोड मैंने SPI0: सक्षम करने के लिए लिखा है (परिभाषित मान spruh73i से हैं।पीडीएफ रजिस्टर वर्णन)

#define CM_PER_BASE  0x44E00000 /* base address of clock control regs */ 
#define CM_PER_SPI0_CLKCTRL  0x4C  /* offset of SPI0 clock control reg */ 

#define SPIO_CLKCTRL_MODE_ENABLE 2   /* value to enable SPI0 clock */ 

int mem;   // handle for /dev/mem 

int InitSlaveSPI(void) // maps the SPI hardware into user space 
{ 
    char *pClockControl; // pointer to clock controlregister block (virtualized by OS) 
    unsigned int value; 

    // Open /dev/mem: 
    if ((mem = open ("/dev/mem", O_RDWR | O_SYNC)) < 0) 
    { 
     printf("Cannot open /dev/mem\n"); 
     return 1; 
    } 
    printf("Opened /dev/mem\n"); 

    // map a pointer to the clock control block: 
    pClockControl = (char *)mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, mem, CM_PER_BASE); 

    if(pClockControl == (char *)0xFFFFFFFF) 
    { 
     printf("Memory map failed. error %i\n", (uint32_t)pClockControl); 
     close(mem); 
     return 2; 
    } 

    value = *(uint32_t *)(pClockControl + CM_PER_SPI0_CLKCTRL); 
    printf("CM_PER_SPI0_CLKCTRL was 0x%08X\n", value); 

    *(uint32_t *)(pClockControl + CM_PER_SPI0_CLKCTRL) = SPIO_CLKCTRL_MODE_ENABLE; 

    value = *(uint32_t *)(pClockControl + CM_PER_SPI0_CLKCTRL); 
    printf("CM_PER_SPI0_CLKCTRL now 0x%08X\n", value); 

    munmap(pClockControl, 4096);    // free this memory map element 

एक बार मैं इस कोड टुकड़ा मार डाला है, मैं SPI0 एक और mmap() सूचक का उपयोग कर रजिस्टरों का उपयोग कर सकते हैं। अगर मैं पहले SPI0 मॉड्यूल घड़ी को सक्षम नहीं करता हूं, तो जब मैं उन एसपीआई रजिस्टरों तक पहुंचने का प्रयास करता हूं तो मुझे बस त्रुटि मिलती है। घड़ी को सक्षम करना लगातार होता है: इस तरह सक्षम होने पर यह तब तक रहता है जब तक आप इसे अक्षम नहीं करते हैं, या हो सकता है कि जब तक आप स्पाइडव का उपयोग न करें और फिर इसे बंद करें, या रीबूट करें। इसलिए यदि आपका एप्लिकेशन आपके द्वारा सक्षम हार्डवेयर के साथ समाप्त हो गया है, तो आप इसे पावर बचाने के लिए अक्षम करना चाहेंगे।

1

REF: madscientist159

// OE: 0 is output, 1 is input 
#define GPIO_OE 0x14d 
#define GPIO_IN 0x14e 
#define GPIO_OUT 0x14f 
should be 
// OE: 0 is output, 1 is input 
#define GPIO_OE 0x4d 
#define GPIO_IN 0x4e 
#define GPIO_OUT 0x4f 

अहस्ताक्षरित int अहस्ताक्षरित चार पता

1

इस विसंगति AM335x चिप में अधूरा पता डिकोडिंग का एक विरूपण साक्ष्य प्रतीत होता है से व्युत्पन्न ऑफसेट पता। यह समझ में आता है कि 0x4D, 0x4E, और 0x4F इन रजिस्टरों तक पहुंचने के लिए मूल पते से ऑफ़सेट के रूप में काम करते हैं। सी/सी ++ पॉइंटर अंकगणित 0x134, 0x138, और 0x13C के सच्चे ऑफसेट बनाने के लिए इन ऑफसेट को 4 से गुणा करता है। हालांकि इन रजिस्टरों की 'छाया' प्रति को 0x14D, 0x14E, और 0x14F के माध्यम से एक्सेस किया जा सकता है। मैंने सत्यापित किया है कि ऑफ़सेट के दोनों सेट काम करते हैं। मैंने 0x24D आदि की कोशिश करने से परेशान नहीं किया

GPIO_CLEARDATAOUT रजिस्टर ऑफसेट 0x64 का उपयोग करके एक्सेस किया जा सकता है और GPIO_SETDATAOUT रजिस्टर ऑफ़सेट 0x65 का उपयोग करके एक्सेस किया जा सकता है।

2

के लिए GPIO बैंकों सक्षम ....

enableClockModules() { 
    // Enable disabled GPIO module clocks. 
    if (mapAddress[(CM_WKUP_GPIO0_CLKCTRL - MMAP_OFFSET)/GPIO_REGISTER_SIZE] & IDLEST_MASK) { 
     mapAddress[(CM_WKUP_GPIO0_CLKCTRL - MMAP_OFFSET)/GPIO_REGISTER_SIZE] |= MODULEMODE_ENABLE; 
     // Wait for the enable complete. 
     while (mapAddress[(CM_WKUP_GPIO0_CLKCTRL - MMAP_OFFSET)/GPIO_REGISTER_SIZE] & IDLEST_MASK); 
    } 
    if (mapAddress[(CM_PER_GPIO1_CLKCTRL - MMAP_OFFSET)/GPIO_REGISTER_SIZE] & IDLEST_MASK) { 
     mapAddress[(CM_PER_GPIO1_CLKCTRL - MMAP_OFFSET)/GPIO_REGISTER_SIZE] |= MODULEMODE_ENABLE; 
     // Wait for the enable complete. 
     while (mapAddress[(CM_PER_GPIO1_CLKCTRL - MMAP_OFFSET)/GPIO_REGISTER_SIZE] & IDLEST_MASK); 
    } 
    if (mapAddress[(CM_PER_GPIO2_CLKCTRL - MMAP_OFFSET)/GPIO_REGISTER_SIZE] & IDLEST_MASK) { 
     mapAddress[(CM_PER_GPIO2_CLKCTRL - MMAP_OFFSET)/GPIO_REGISTER_SIZE] |= MODULEMODE_ENABLE; 
     // Wait for the enable complete. 
     while (mapAddress[(CM_PER_GPIO2_CLKCTRL - MMAP_OFFSET)/GPIO_REGISTER_SIZE] & IDLEST_MASK); 
    } 
    if (mapAddress[(CM_PER_GPIO3_CLKCTRL - MMAP_OFFSET)/GPIO_REGISTER_SIZE] & IDLEST_MASK) { 
     mapAddress[(CM_PER_GPIO3_CLKCTRL - MMAP_OFFSET)/GPIO_REGISTER_SIZE] |= MODULEMODE_ENABLE; 
     // Wait for the enable complete. 
     while (mapAddress[(CM_PER_GPIO3_CLKCTRL - MMAP_OFFSET)/GPIO_REGISTER_SIZE] & IDLEST_MASK); 
    } 
} 

कहाँ ...

MMAP_OFFSET = 0x44C00000

MMAP_SIZE = 0x481AEFFF - MMAP_OFFSET

GPIO_REGISTER_SIZE = 4

MODULEMODE_ENABLE = 0x02

IDLEST_MASK = (0x03 < < 16)

CM_WKUP = 0x44E00400

CM_PER = 0x44E00000

CM_WKUP_GPIO0_CLKCTRL = (CM_WKUP + 0x8)

CM_PER_GPIO1_CLKCTRL = (CM_PER + 0xAC)

सीएम_PER_GPIO2_CLKCTRL = (सीएम_PER + 0xB0)

सीएम_PER_GPIO3_CLKCTRL = (सीएम_PER + 0xB4)

मैंने small library लिखा है जो शायद आपको रुचि हो सकती है। फिलहाल केवल डिजिटल पिन के साथ काम करता है।

सादर

+1

आपको सी ++ में विनाशक को कॉल करने की आवश्यकता नहीं है, ऑब्जेक्ट को नष्ट होने पर यह स्वचालित रूप से किया जाता है (या तो आवंटित ऑब्जेक्ट्स के लिए दायरा छोड़कर या हेप आवंटित ऑब्जेक्ट्स के लिए डिलीट का उपयोग करते समय)। इसी प्रकार, आपको बिना हस्ताक्षर किए गए चार में एनम निकालने की आवश्यकता नहीं है, यह वास्तव में तेज़ नहीं है, क्योंकि संकलक कुछ रजिस्टरों को "स्थिरांक" स्टोर करने के लिए आरक्षित करेगा, इस प्रकार अगले कोड के लिए अपनी आजादी को कम कर देगा, जिससे कम कुशल धक्का/पॉप हो जाएगा ढेर योजना। – xryl669

संबंधित मुद्दे