element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Raspberry Pi
  • Products
  • More
Raspberry Pi
Raspberry Pi Forum Can GPIO pins generate interrupts?
  • Blog
  • Forum
  • Documents
  • Quiz
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Raspberry Pi to participate - click to join for free!
Featured Articles
Announcing Pi
Technical Specifications
Raspberry Pi FAQs
Win a Pi
Raspberry Pi Wishlist
Actions
  • Share
  • More
  • Cancel
Forum Thread Details
  • Replies 23 replies
  • Subscribers 667 subscribers
  • Views 5342 views
  • Users 0 members are here
  • raspberry_pi
Related

Can GPIO pins generate interrupts?

jrychter
jrychter over 13 years ago

I looked at the example code for doing I/O using the GPIO pins, but — I need to be able to count impulses (between 0.01 and 250Hz), which isn't really doable with GPIO polling on a multitasking OS.

 

Are the GPIO pins capable of generating interrupts?

  • Sign in to reply
  • Cancel
Parents
  • Former Member
    Former Member over 13 years ago

    Yes they can generate interrupts, and just yesterday some example code was posted. Note, I do not know whether 250 Hz pulses (4 msec interval) will always be reliable, depending on what other operations the R-Pi is doing (SD card I/O, USB and Ethernet, etc.) although you may be able to change interrupt priority if needed.

     

    Support for GPIO-driven interrupts

    http://www.raspberrypi.org/phpBB3/viewtopic.php?f=44&t=7509

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • jrychter
    jrychter over 13 years ago in reply to Former Member

    Thanks for the link!

     

    I thought about it some more, and then remembered that this is Linux, where you deal with interrupts in kernel space, possibly creating an interface to user-space.

     

    I think for the moment it is actually simpler for me to use a MSP430-based Launchpad board to do the counting and interface it via SPI to the Raspberry.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • rdolson
    rdolson over 12 years ago in reply to jrychter

    I was doing some experiments last night with a 555 timer set for around 1500 Hz connected to a GPIO on the pi, and a modification of the irq demo program linked from this page:

     

    http://www.raspberrypi.org/phpBB3/viewtopic.php?f=44&t=7509

     

    I used gettimeofday to time the interval between interrupts, and was getting numbers quite close (within 1% probably) of the frequency based on what I was reading from the frequency counter on my multimeter also attached to that input. CPU utilization was around 6-7%.

     

    --bob

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • rew
    rew over 12 years ago in reply to rdolson

    That's a very interesting experiment. The question then is: Why are you getting a 1% difference?

     

    Is the crystal on the RPI inaccurate? Are you missing interrupts? Is the multimeter inaccurate?

     

    If you are just measuring the interval to be about 700 microseconds, and then doing 1/interval and getting 1500+/- 15 each time, you're not missing interrupts. If you count interrupts for a long enough period, maybe 1 day of wallclock or ntp-synced time, then you should be able to calibrate the crystal of the raspberry pi.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • rdolson
    rdolson over 12 years ago in reply to rew

    The inter-interrupt times wandered around a central value (that wander is what I was estimating as 1%). This was a very sloppy experiment so I don't have any stored data to look back on. My assumption is that there's variation in the latency between the low-level GPIO interrupt and the time at which the userland code is able to take action, since the linux scheduler is involved (and there's a context switch required etc).

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • Former Member
    Former Member over 12 years ago in reply to rdolson

    Hi,

     

    Sorry to "break in" to this discussion but I've been trying to get the "gpio-irq-demo" program to work with no success, and it appears that you've got it (or some version of it) working just fine :-).

     

    i fetched the code from here: http://dev.mrkva.eu/rpi/gpio-irq-demo.c

     

    The poll() call always just times out - the program doesn't detect the rising or falling signal edge.  Its probably something silly or obvious, but I've spent a few hours and no luck - can you help?

     

    I'm running on the latest apt-get updated Raspbian Wheezy kernel on a Rev 2 Pi which i understand includes the kernel patch for interrupt support (right?).  I'm using GPIO pin 17 (aka WiringPi pin 0) and have verified that i can read it using the "gpio" utility.  e.g.

     

    $ gpio mode 0 in

    $ gpio read 0 # returns the correct 0 or 1 if pin is at ground or Vcc

     

    # now pull the pin to ground

     

    $ gpio edge 0 rising

     

    $ ./gpio-irq-demo 17 # program is using the BCM pin numbers...

    value is 0

    timeout

    timeout

     

    # but when i pull the pin up to Vcc, i don't get the "interrupt, value is: " message

     

    Any idea what is wrong?

     

    thanks for your help!

     

    Don

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • rdolson
    rdolson over 12 years ago in reply to Former Member

    Hm, not offhand. However I did do the pin configuration directly using the /sys/class/gpio interface, not through wiring. Something like

     

    echo 17 > /sys/class/gpio/export

    echo rising > /sys/class/gpio/gpio17/edge

     

    I forget if I had to set the pin direction as well, but I suspect it defaults to in.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • Former Member
    Former Member over 12 years ago in reply to rdolson

    hmmm.  i was getting "permission denied" (even with sudo) trying to use the "echo" commands with /sys/class/gpio so opted for the gpio route.  I think I saw some discussions somewhere about how to work around this, but can you let me know what you did?

     

    It would also be really appreciated if you could send me your interrupt code - I'm new to this site, not sure how that is done...

     

    thx

    Don

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • rdolson
    rdolson over 12 years ago in reply to Former Member

    You do need to run the commands as root ("sudo bash" will get you a root shell). My code is sitting on my pi which is powered off on my workbench - will put a copy up when I have it online next.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
Reply
  • rdolson
    rdolson over 12 years ago in reply to Former Member

    You do need to run the commands as root ("sudo bash" will get you a root shell). My code is sitting on my pi which is powered off on my workbench - will put a copy up when I have it online next.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
Children
  • Former Member
    Former Member over 12 years ago in reply to rdolson

    Eueka!  "sudo bash" did the trick - thanks.  Next step is to use threads so my main program can continue to run.  I will have to handle multiple inputs - I need to count pulses coming in on several GPIO pins.  I think i saw some example code (wfi.c) that does this, but I couldn't get it to work - probably for the same reason that I couldn't get gpio-irq-demo.c to work.  Good news is that the frequency i need to handle is really low - not more than a few pulses per second.

     

    If you have any example code that you think might help, let me know...

     

    My application, by the way, is an aquarium control system - I will be building sensors that will allow me to count CO2 bubbles (for a CO2 injector) and water drips (for a Nitrate filter), read Ph and ORP sensors, and control some valves to maintain specific levels.

     

    Thanks again for your assistance. 

    Don

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • rdolson
    rdolson over 12 years ago in reply to Former Member

    This sounds like a classic event driven application (you have events coming in triggered by interrupts from your bubble/drip sensors, and presumably from one or more timers that drive periodic checks and maintenance that will turn valves on and off).

     

    If I was writing this software for myself I'd probably not write it in C but rather use something like perl and the timer / event libraries that are available for it. I'm sure there are such event libraries for any of the scripting languages. The advantage of doing it with one of these languages is that the volume of code will be much smaller and the logic will be easier to represent. With the rates you are talking about performance will not be a problem at all.

     

    --bob

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • Former Member
    Former Member over 12 years ago in reply to rdolson

    Hi Bob,

     

    you are almost certainly correct, but being an old C programmer with minimal Perl expertise, I'm just more comfortable with that language. Something about an old dog and new tricks?

     

    Meanwhile, I've made good progress in C - i am able to read a temperature sensor (varistor) via an adc on the I2C bus, handle a pulse counter using edge-triggered interrupts and poll(), read an ORP probe via the Pi's serial port, and turn off/on some relays.  Next step is to start using threads to take the pulse counter out of the main program loop, put the whole thing together, and actually hook it up to the tank...

     

    regards

    Don

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • rdolson
    rdolson over 12 years ago in reply to Former Member

    Very cool ... if you're an old C hand I retract my suggestion :-)

     

    Being a conservative sort I'd still bag the threads and make the application event driven though, since you can set up timers to be triggered and hang the whole program around a loop around the poll() call.

     

    -bob

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • Former Member
    Former Member over 12 years ago in reply to rdolson

    Not sure I'm following your suggestion... My idea is to create a thread that does the poll(), ie is event-driven and wakes up with activity on the pin to increment a counter.  Thus the main program thread can handle the main control loop.

     

    the gpio input is set up for edge-triggered interrupts.

     

    Once i finish it (in a few days i hope, and assuming it actually works :-)) should I post my code here so you can review and clarify/make suggestions?

     

    Thx

    Don

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • rdolson
    rdolson over 12 years ago in reply to Former Member

    What would your main program be doing while waiting for events from the thread? If it's only acting on external events, or on timeouts ("if no bubbles in 3 minutes do something"), you don't need a separate thread and the associated synchonization - you can use the poll mechanism to set up the timeouts.

     

    I'd be happy to take a look at the code.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • Former Member
    Former Member over 12 years ago in reply to rdolson

    Hi Robert,

     

    Here's the current code - no threads.  Note that the program doesn't do anything useful yet - I'm still just playing around to figure out the basic structure.

     

    My intent is to move the poll() into a thread, and change the timeout value so it waits forever.  Then the main program thread would have a separate loop (perhaps running once per 5 minutes - aquariums don't change very quickly) where I check the counts (i believe i will need to use semaphone to protect read/write of the counts) and do stuff based on a control algorithm.

     

    The main loop would be augmented to deal with the other sensors as well - temperature, ORP, Ph, and so on.

     

    It seems to me that if I leave the poll() and pulse counter in my main program, I can't easily control the timing of my main control loop.  Let me know if I'm missing something...

     

    regards

    Don

     

     

    /*

    * counts pulses on one or more gpio inputs using poll()

    * based on code pilfered from various places.  will add credits later...

    * uses wiringPi library, currently must run as root.  will deal with that later...

    */

     

    #include <stdio.h>

    #include <stdlib.h>

    #include <string.h>

    #include <errno.h>

    #include <unistd.h>

    #include <fcntl.h>

    #include <poll.h>

    #include <string.h>

    #include <pthread.h>

     

    extern int gpio_export(unsigned int gpio);

    extern int gpio_unexport(unsigned int gpio);

    extern int gpio_set_dir(unsigned int gpio, unsigned int out_flag);

    extern int gpio_set_value(unsigned int gpio, unsigned int value);

    extern int gpio_get_value(unsigned int gpio, unsigned int *value);

    extern int gpio_set_edge(unsigned int gpio, char *edge);

    extern int gpio_fd_open(unsigned int gpio);

    extern int gpio_fd_close(int fd);

     

    /****************************************************************

    * Constants

    ****************************************************************/

     

    #define POLL_TIMEOUT (3 * 1000) /* 3 seconds */

    #define MAX_BUF 64

    #define MAX_PINS 16

     

    /****************************************************************

    * Main

    ****************************************************************/

    int main(int argc, char **argv, char **envp)

    {

        struct pollfd fdset[MAX_PINS];

        int nfds = argc-1;

        int gpio_fd[MAX_PINS], timeout, rc;

        char *buf[MAX_BUF];

        unsigned int gpio[MAX_PINS];

        int i;

        int count[MAX_PINS];

     

        if (argc < 2) {

            printf("Usage: %s <gpio-pin> .. <gpio-pin>\n", argv[0]);

            printf("Waits for a rising edge on the specified GPIO pins\n");

            exit(-1);

        }

     

        // set up

        for (i=0; i < nfds; i++) {

            gpio[i] = atoi(argv[i+1]);

            gpio_export(gpio[i]);

            gpio_set_dir(gpio[i], 0);  // input

            gpio_set_edge(gpio[i], "falling");

            gpio_fd[i] = gpio_fd_open(gpio[i]);

            printf("set up gpio input %d\n", gpio[i]);

            count[i] = 0;

        }

     

        timeout = POLL_TIMEOUT;

     

        memset((void*)fdset, 0, sizeof(fdset));

     

        for (i=0; i < nfds; i++) {

            fdset[i].fd = gpio_fd[i];

            fdset[i].events = POLLPRI;

        }

     

        while (1) {

            // poll() returns with 0 after a timeout or > 1 for activity on any of the specified pins

            rc = poll(fdset, nfds, timeout);

     

            if (rc > 0) { // activity occurred on one or more pins

                for (i=0; i<nfds; i++) {  // check each file descriptor

                    if (fdset[i].revents & POLLPRI) {

                        // there was activity on this pin

                        read(fdset[i].fd, buf, MAX_BUF);  // this clears the interrupt, i think?

                        count[i]++;

                        printf("GPIO %d interrupt %d\n", gpio[i], count[i]);

                    } // if

                    fflush(stdout);

                } // for

            } else {

                if (rc < 0) {

                    printf("poll() failed with %d!\n", errno);

                    return -1;

                } // if

            } // else

        } // while

     

        // clean up

        for (i=0; i<nfds; i++) {

            gpio_fd_close(gpio_fd[i]);

        }

     

        return 0;

    }

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • rdolson
    rdolson over 12 years ago in reply to Former Member

    Here's my code with some fairly crude high-level timer support. The idea here is that there are a number of timers that can be registered by the user code; you do this by calling

     

         add_timer(when, interval, oneshot, callback)

     

    when is a double denoting when the timer should trigger. The function get_now() returns a double representing the current time (in seconds).

     

    interval is a double denoting the time between invocations of interval timers - timers that trigger indefinitely until disabled.

     

    oneshot is an int that if true, says this is a one shot timer, and if false (0), says this is a repeating timer.

     

    callback is a pointer to a function that looks like

     

         int callback_function(ptimer_t *timer);

     

    the timer parameter is a pointer to the underlying timer object, which can safely be ignored. If the timer is a repeating timer, the return value from the callback affects if the timer is removed or not. Return 1 and the timer will keep getting called, return 0 and the timer is removed.

     

    In this example the poll will wake up the code to handle interrupts if there are interrupts, and will otherwise wake up to handle any timers that are registered.

     

    --bob

     

     

    #include <stdio.h>

    #include <poll.h>

    #include <stdlib.h>

    #include <fcntl.h>

    #include <string.h>

    #include <sys/time.h>

     

     

    #define GPIO_FN_MAXLEN          32

    #define POLL_TIMEOUT          1000

    #define RDBUF_LEN          5

     

     

    typedef struct ptimer ptimer_t;

     

     

    typedef int (*ptimer_cb)(ptimer_t *);

     

     

    struct ptimer

    {

        double when;

        double interval;

        int oneshot;

        ptimer_cb cb;

        ptimer_t *next;

    };

     

     

    static ptimer_t timers;

     

     

    double times[1000];

    int n;

     

     

    double get_now()

    {

        struct timeval tv;

        gettimeofday(&tv, 0);

        return (double) tv.tv_sec + (double) tv.tv_usec * 1e-6;

    }

     

     

    void add_timer(double when, double interval, int oneshot, ptimer_cb cb)

    {

        ptimer_t *t = (ptimer_t *) calloc(sizeof(ptimer_t), 1);

        t->when = when;

        t->interval = interval;

        t->oneshot = oneshot;

        t->cb = cb;

        t->next = timers.next;

        timers.next = t;

    }

     

     

    int hi(ptimer_t *t)

    {

        printf("Hi!\n");

        return 1;

    }

     

     

    int hi2(ptimer_t *t)

    {

        printf("Hi 2!\n");

        return 1;

    }

     

     

    int hi3(ptimer_t *t)

    {

        printf("Hi 3!\n");

        return 1;

    }

     

     

    double least_time()

    {

        ptimer_t *t = timers.next;

        /* we only invoke this routine if there is at least one timer. */

        double least = t->when;

        t = t->next;

     

     

        while (t)

        {

              if (t->when < least)

              {

                  least = t->when;

              }

              t = t->next;

        }

        return least;

    }

     

     

    void call_timers()

    {

        ptimer_t *t, *last_t;

        double now = get_now();

        last_t = &timers;

        while (last_t->next)

        {

              t = last_t->next;

              if (t->when <= now)

              {

                  int keep = (t->cb)(t);

                  if (t->oneshot || !keep)

                  {

                        t = t->next;

                        free(last_t->next);

                        last_t->next = t;

                  }

                  else if (!t->oneshot)

                  {

                        t->when += t->interval;

                  }

              }

              last_t = t;

        }

    }

     

     

    int main(int argc, char **argv) {

        char fn[GPIO_FN_MAXLEN];

        int fd,ret;

        struct pollfd pfd;

        char rdbuf[RDBUF_LEN];

     

     

        timers.next = 0;

        add_timer(get_now() + 1.0, 1.0, 0, hi);

        add_timer(get_now() + 2.0, 1.7, 0, hi2);

        add_timer(get_now() + 2.1, 0.0, 1, hi3);

     

     

        memset(rdbuf, 0x00, RDBUF_LEN);

        memset(fn, 0x00, GPIO_FN_MAXLEN);

     

     

        if(argc!=2) {

                  printf("Usage: %s <GPIO>\nGPIO must be exported to sysfs and have enabled edge detection\n", argv[0]);

                  return 1;

        }

        snprintf(fn, GPIO_FN_MAXLEN-1, "/sys/class/gpio/gpio%s/value", argv[1]);

        fd=open(fn, O_RDONLY);

        if(fd<0) {

                  perror(fn);

                  return 2;

        }

        pfd.fd=fd;

        pfd.events=POLLPRI;

       

        ret=read(fd, rdbuf, RDBUF_LEN-1);

        if(ret<0) {

                  perror("read()");

                  return 4;

        }

        printf("value is: %s\n", rdbuf);

       

        n = 0;

        while(1) {

                  memset(rdbuf, 0x00, RDBUF_LEN);

                  lseek(fd, 0, SEEK_SET);

                  struct timespec wait;

                  if (timers.next)

                  {

                        double l = least_time();

                        double lwait = l - get_now();

                        if (lwait < 0)

                        {

                            wait.tv_sec = 0L;

                            wait.tv_nsec = 1000;

                        }

                        else

                        {

                            wait.tv_sec = (long) lwait;

                            wait.tv_nsec = (long) ((lwait - (double) wait.tv_sec) * 1e9);

                        }

                  }

                  else

                  {

                        wait.tv_sec = 1L;

                        wait.tv_nsec = 0L;

                  }

     

     

     

     

                  ret = ppoll(&pfd, 1, &wait, 0);

                  if(ret<0) {

                            perror("poll()");

                            close(fd);

                            return 3;

                  }

                  if(ret==0) {

                            printf("timeout\n");

                            call_timers();

                            continue;

                  }

                  ret=read(fd, rdbuf, RDBUF_LEN-1);

                  if(ret<0) {

                            perror("read()");

                            return 4;

                  }

    /*

                  struct timeval tv;

                  gettimeofday(&tv, 0);

                  if (n < 999)

                  {

                            times[n++] = (double) tv.tv_sec + (double) tv.tv_usec * 1e-6;

                  }

                  else

                  {

                            int i;

                            for (i = 1; i < 1000; i++)

                            {

                                      printf("%lf\n", 1.0/ (times[i] - times[i-1]));

                            }

                            n = 0;

                  }

    */

                  printf("interrupt, value is: %s\n", rdbuf);

        }

        close(fd);

        return 0;

    }

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • Former Member
    Former Member over 12 years ago in reply to rdolson

    Thanks for this - I'll probably end up incorporating elements of this - I'll need to set up handlers that are invoked at different intervals.  So, much appreciated.

     

    Actually, I forgot to mention a primary reason for using a separate thread to handle the poll() for the GPIO edge interrupt, which is that this will enable me to change (raise) the priority of this relative to the rest of the code.  The app will eventually be writing data to a mysql database, talking to an LCD display, driving relays, etc., and I don't want it to miss any interrupts when counting pulses.

     

    regards

    Don

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • Former Member
    Former Member over 12 years ago in reply to Former Member

    I have done little programming lately, but seem to remember from PC Linux there may be a way to tell if you missed any interrupts from /proc/interrupts.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube