build oscilloscope

Upload: sara-shan

Post on 15-Feb-2018

254 views

Category:

Documents


5 download

TRANSCRIPT

  • 7/23/2019 Build Oscilloscope

    1/54

    SKILL LEVEL : ADVANCED

    Daniel Pelikan

    Guest Writer

    KERNEL MODULEto reach analogue sampling limit

    Building an oscilloscope with aRaspberry Pi

    An oscilloscope can be very useful to analyse a circuit orexperiment. Slow signals can be sampled with a soundcard and xoscopehttp://xoscope.sourceforge.net/However, sound cards cannot run at sampling frequenciesabove 100kHz.

    To achieve higher sampling rates, I tried using an Arduino.With the Arduino internal Analog Digital Converter (ADC), Iwas able to reach 1,000,000 samples every second(1MSPS). Next, I tried using the Arduino with an externalADC, which reached around 5MSPS. However, this wasstill too slow for my application and I searched for a cheapway to reach a higher sampling speed.

    I decided to try the Raspberry Pi, which provides 17General Purpose Input Output (GPIO) pins that can beused to interface with ADC chips. Although we could usean ADC that connects via SPI or I2C to the Raspberry Pi,these protocols result in sampling rates that are as slow asa sound card readout or slower. One needs to use an ADCthat has a parallel data output, which can be connected to

    a Raspberry Pi.

    Parallel ADC and realtime readout

    A parallel ADC can be used to take a sample on the risingedge of a clock signal and output the sample on the datapins on the falling edge. The aim is to clock the ADC at ourrequired sample rate and read all of the data pins betweeneach sample.

    The Raspberry Pi is a general purpose computer that canrun a Linux operation system. However, Linux operating

    systems do not normally run processes in realtime. This isbecause the operating system listens for inputs from otherdevices, rather than just processing one command at atime. When reading an external ADC, one needs to make

    sure that the time between each sample point is the same.Without a realtime operating system, this is notguaranteed.

    After a lot of tests and a lot of reading about interrupts andprocess control in Linux, I decided to write a Linux kernelmodule to try to solve the realtime readout problem.

    A Linux kernel moduleWriting a Linux kernel module provides the possibility toperform low level hardware operations. We need to runwith the highest possible priority, reading the GPIO registerwith the system interrupts disabled for as short a time aspossible.

    Compiling a kernel module

    The text that follows assumes that a Raspberry Pi is beingused to compile the Linux kernel. However, another LinuxPC can be used to perform cross-compilation to speed up

    the process:http://elinux.org/RPi_Kernel_Compilation

    To set up the build environment correctly, copy the Bashscript from the top of the next page into a file, make itexecutable and then run it.

    Before proceeding, it may be useful to read over someLinux kernel development documentation:http://www.tldp.org/LDP/lkmpg/2.6/html/lkmpg.html

    To read and write registers on the Raspberry Pi, we needto know their addresses in memory. This information can

    be found in the BCM2835-ARM-Peripheralsdocumentation:http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf

    http://xoscope.sourceforge.net/http://elinux.org/RPi_Kernel_Compilationhttp://www.tldp.org/LDP/lkmpg/2.6/html/lkmpg.htmlhttp://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdfhttp://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdfhttp://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdfhttp://www.tldp.org/LDP/lkmpg/2.6/html/lkmpg.htmlhttp://elinux.org/RPi_Kernel_Compilationhttp://xoscope.sourceforge.net/
  • 7/23/2019 Build Oscilloscope

    2/55

    Writing the kernel module

    Create a C file called S c o p e - d r v . c that contains:

    # i n c l u d e < l i n u x / k e r n e l . h >

    # i n c l u d e < l i n u x / m o d u l e . h >

    # i n c l u d e < l i n u x / f s . h >

    # i n c l u d e < a s m / u a c c e s s . h >

    # i n c l u d e < l i n u x / t i m e . h >

    # i n c l u d e < l i n u x / i o . h >

    # i n c l u d e < l i n u x / v m a l l o c . h >

    i n t i n i t _ m o d u l e ( v o i d ) ;

    v o i d c l e a n u p _ m o d u l e ( v o i d ) ;

    s t a t i c i n t d e v i c e _ o p e n ( s t r u c t i n o d e * , s t r u c t f i l e * ) ;

    s t a t i c i n t d e v i c e _ r e l e a s e ( s t r u c t i n o d e * , s t r u c t f i l e * ) ;

    s t a t i c s s i z e _ t d e v i c e _ r e a d ( s t r u c t f i l e * , c h a r * , s i z e _ t ,

    l o f f _ t * ) ;

    s t a t i c s s i z e _ t d e v i c e _ w r i t e ( s t r u c t f i l e * , c o n s t c h a r * ,

    s i z e _ t , l o f f _ t * ) ;

    # d e f i n e S U C C E S S 0

    # d e f i n e D E V I C E _ N A M E " c h a r d e v " / * D e v i c e n a m e * /

    # d e f i n e B U F _ L E N 8 0 / * M a x i m u m l e n g t h o f d e v i c e m e s s a g e * /

    To set address values and allow simpler registermanipulation in the C code, append the preprocessormacros at the bottom of this page to the C source file.More information on these macros can be found at:http://www.pieter-jan.com/node/15

    The next piece of code that should be added to the C

    source file defines the GPIO connections that are used toconnect to the ADC. For this article, a six bit ADC was

    used. Therefore, six GPIO connections are needed perADC:

    / * N u m b e r o f s a m p l e s t o c a p t u r e * /

    # d e f i n e S A M P L E _ S I Z E 1 0 0 0 0

    / * D e f i n e G P I O P i n s * /

    / * A D C 1 * /

    # d e f i n e B I T 0 _ P I N 7

    # d e f i n e B I T 1 _ P I N 8

    # d e f i n e B I T 2 _ P I N 9

    # d e f i n e B I T 3 _ P I N 1 0

    # d e f i n e B I T 4 _ P I N 1 1

    # d e f i n e B I T 5 _ P I N 2 5

    / * A D C 2 * /

    # d e f i n e B I T 0 _ P I N 2 1 7

    # d e f i n e B I T 1 _ P I N 2 1 8

    # d e f i n e B I T 2 _ P I N 2 2 2

    # d e f i n e B I T 3 _ P I N 2 2 3

    # d e f i n e B I T 4 _ P I N 2 2 4

    # d e f i n e B I T 5 _ P I N 2 2 7

    The numbering scheme used follows the BCM numberingscheme given at:http://elinux.org/RPi_Low-level_peripherals

    / * S e t t i n g s a n d m a c r o s f o r t h e G P I O c o n n e c t i o n s * /

    # d e f i n e B C M 2 7 0 8 _ P E R I _ B A S E 0 x 2 0 0 0 0 0 0 0

    # d e f i n e G P I O _ B A S E ( B C M 2 7 0 8 _ P E R I _ B A S E + 0 x 2 0 0 0 0 0 ) / * G P I O c o n t r o l l e r * /

    # d e f i n e I N P _ G P I O ( g ) * ( g p i o . a d d r + ( ( g ) / 1 0 ) ) & = ~ ( 7 < < ( ( ( g ) % 1 0 ) * 3 ) )

    # d e f i n e S E T _ G P I O _ A L T ( g , a ) * ( g p i o . a d d r + ( ( ( g ) / 1 0 ) ) ) | = ( ( ( a ) < = 3 ? ( a ) + 4 : ( a ) = = 4 ? 3 : 2 ) < < ( ( ( g ) % 1 0 ) * 3 ) )

    / * G P I O c l o c k * /

    # d e f i n e C L O C K _ B A S E ( B C M 2 7 0 8 _ P E R I _ B A S E + 0 x 0 0 1 0 1 0 0 0 )

    # d e f i n e G Z _ C L K _ B U S Y ( 1 < < 7 )

    # ! / b i n / b a s h

    # A s c r i p t t o s e t u p t h e R a s p b e r r y P i f o r a k e r n e l b u i l d

    F V = ` z g r e p " * f i r m w a r e a s o f " / u s r / s h a r e / d o c / r a s p b e r r y p i - b o o t l o a d e r / c h a n g e l o g . D e b i a n . g z | h e a d - 1 | a w k ' { p r i n t $ 5 } ' `

    m k d i r - p k _ t m p / l i n u x

    w g e t h t t p s : / / r a w . g i t h u b . c o m / r a s p b e r r y p i / f i r m w a r e / $ F V / e x t r a / g i t _ h a s h - O k _ t m p / g i t _ h a s h

    w g e t h t t p s : / / r a w . g i t h u b . c o m / r a s p b e r r y p i / f i r m w a r e / $ F V / e x t r a / M o d u l e . s y m v e r s - O k _ t m p / M o d u l e . s y m v e r s

    H A S H = ` c a t k _ t m p / g i t _ h a s h `

    w g e t - c h t t p s : / / g i t h u b . c o m / r a s p b e r r y p i / l i n u x / t a r b a l l / $ H A S H - O k _ t m p / l i n u x . t a r . g z

    c d k _ t m p

    t a r - x z f l i n u x . t a r . g z

    K V = ` u n a m e - r `

    s u d o m v r a s p b e r r y p i - l i n u x * / u s r / s r c / l i n u x - s o u r c e - $ K V

    s u d o l n - s / u s r / s r c / l i n u x - s o u r c e - $ K V / l i b / m o d u l e s / $ K V / b u i l d

    s u d o c p M o d u l e . s y m v e r s / u s r / s r c / l i n u x - s o u r c e - $ K V /

    s u d o z c a t / p r o c / c o n f i g . g z > / u s r / s r c / l i n u x - s o u r c e - $ K V / . c o n f i g

    c d / u s r / s r c / l i n u x - s o u r c e - $ K V /

    s u d o m a k e o l d c o n f i g

    s u d o m a k e p r e p a r e

    s u d o m a k e s c r i p t s Bash script

    Scope-drv.c (1/10)

    Scope-drv.c (2/10)

    Scope-drv.c (3/10)

    http://www.pieter-jan.com/node/15http://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://elinux.org/RPi_Low-level_peripheralshttp://www.pieter-jan.com/node/15
  • 7/23/2019 Build Oscilloscope

    3/56

    Now add the remaining function and variable definitionsgiven below to the C file.

    s t r u c t b c m 2 8 3 5 _ p e r i p h e r a l {

    u n s i g n e d l o n g a d d r _ p ;

    i n t m e m _ f d ;

    v o i d * m a p ;

    v o l a t i l e u n s i g n e d i n t * a d d r ;

    } ;

    s t a t i c i n t m a p _ p e r i p h e r a l ( s t r u c t b c m 2 8 3 5 _ p e r i p h e r a l * p ) ;

    s t a t i c v o i d u n m a p _ p e r i p h e r a l ( s t r u c t b c m 2 8 3 5 _ p e r i p h e r a l * p ) ;

    s t a t i c v o i d r e a d S c o p e ( v o i d ) ; / * R e a d a s a m p l e * /

    s t a t i c i n t M a j o r ; / * M a j o r n u m b e r s e t f o r d e v i c e d r i v e r * /

    s t a t i c i n t D e v i c e _ O p e n = 0 ; / * S t o r e d e v i c e s t a t u s * /

    s t a t i c c h a r m s g [ B U F _ L E N ] ;

    s t a t i c c h a r * m s g _ P t r ;

    s t a t i c u n s i g n e d c h a r * b u f _ p ;

    s t a t i c s t r u c t f i l e _ o p e r a t i o n s f o p s = {

    . r e a d = d e v i c e _ r e a d ,

    . w r i t e = d e v i c e _ w r i t e ,

    . o p e n = d e v i c e _ o p e n ,

    . r e l e a s e = d e v i c e _ r e l e a s e

    } ;

    s t a t i c s t r u c t b c m 2 8 3 5 _ p e r i p h e r a l m y c l o c k = { C L O C K _ B A S E } ;

    s t a t i c s t r u c t b c m 2 8 3 5 _ p e r i p h e r a l g p i o = { G P I O _ B A S E } ;

    s t r u c t D a t a S t r u c t {

    u i n t 3 2 _ t B u f f e r [ S A M P L E _ S I Z E ] ;

    u i n t 3 2 _ t t i m e ;

    } ;

    s t r u c t D a t a S t r u c t d a t a S t r u c t ;

    s t a t i c u n s i g n e d c h a r * S c o p e B u f f e r S t a r t ;

    s t a t i c u n s i g n e d c h a r * S c o p e B u f f e r S t o p ;

    The first part of this code defines a struct to hold theaddress information. A pointer of this struct type is passedto the m a p _ p e r i p h e r a l ( ) and u n m a p _ p e r i p h e r a l ( ) functions, which are used to map the hardware registersinto memory and release the mapping.

    Them y c l o c k

    andg p i o

    structs are assigned the registeraddresses of the GPIO and clock pins, such that they

    might be used later. TheD a t a S t r u c

    t is defined to holdthe time and voltage data read from the ADC. The timeinformation is needed in order to calculate the timebetween each sample. In addition two pointersS c o p e B u f f e r S t a r t and S c o p e B u f f e r S t o p aredefined for later use.

    Now that all of the function declarations have been made,the implementation of each function must be added tocomplete the kernel module.

    Memory mapping functions

    s t a t i c i n t m a p _ p e r i p h e r a l ( s t r u c t b c m 2 8 3 5 _ p e r i p h e r a l * p ) {

    p - > a d d r = ( u i n t 3 2 _ t * ) i o r e m a p ( G P I O _ B A S E , 4 1 * 4 ) ;

    r e t u r n 0 ;

    }

    s t a t i c v o i d u n m a p _ p e r i p h e r a l ( s t r u c t b c m 2 8 3 5 _ p e r i p h e r a l * p ) {

    i o u n m a p ( p - > a d d r ) ; / / u n m a p t h e a d d r e s s

    }

    Add these two functions to the end of the C source file.

    The readScope function

    Ther e a d S c o p e ( )

    function is responsible for the ADCreadout. Add the code below to the end of the C file.

    s t a t i c v o i d r e a d S c o p e ( ) {

    i n t c o u n t e r = 0 ;

    s t r u c t t i m e s p e c t s _ s t a r t , t s _ s t o p ;

    / * d i s a b l e I R Q * /

    l o c a l _ i r q _ d i s a b l e ( ) ;

    l o c a l _ f i q _ d i s a b l e ( ) ;

    g e t n s t i m e o f d a y ( & t s _ s t a r t ) ; / * G e t s t a r t t i m e i n n s * /

    / * t a k e s a m p l e s * /

    w h i l e ( c o u n t e r < S A M P L E _ S I Z E ) {

    d a t a S t r u c t . B u f f e r [ c o u n t e r + + ] = * ( g p i o . a d d r + 1 3 ) ;

    }

    g e t n s t i m e o f d a y ( & t s _ s t o p ) ; / * G e t s t o p t i m e i n n s * /

    / * e n a b l e I R Q * /

    l o c a l _ f i q _ e n a b l e ( ) ;

    l o c a l _ i r q _ e n a b l e ( ) ;

    / * S t o r e t h e t i m e d i f f e r e n c e i n n s * /

    d a t a S t r u c t . t i m e =

    t i m e s p e c _ t o _ n s ( & t s _ s t o p ) - t i m e s p e c _ t o _ n s ( & t s _ s t a r t ) ;

    b u f _ p = ( u n s i g n e d c h a r * ) & d a t a S t r u c t ;

    S c o p e B u f f e r S t a r t = ( u n s i g n e d c h a r * ) & d a t a S t r u c t ;

    S c o p e B u f f e r S t o p = S c o p e B u f f e r S t a r t +

    s i z e o f ( s t r u c t D a t a S t r u c t ) ;

    }

    The first action in this function is to disable all interrupts toprovide realtime readout. It is very important that the timewhile the interrupts are disabled is minimised, since theinterrupts are needed for many other Linux processes suchas the network connections and other file operations.

    Reading 10,000 samples takes approximately 1ms, whichis small enough not to cause interrupt related problems.

    Before reading out the ADC, the current time in nanoseconds is stored. Then the full GPIO register is read out10,000 times and the data are saved in d a t a S t r u c t .After the readout, the current time is requested again andthe interrupts are enabled again. The time between eachsample point is calculated from the time difference dividedby 10,000.

    Scope-drv.c (4/10)Scope-drv.c (5/10)

    Scope-drv.c (6/10)

  • 7/23/2019 Build Oscilloscope

    4/57

    The init_module function

    In order to make a kernel module work, the module needssome special entry functions. One of these functions is the

    i n i t _ m o d u l e ( ) , which is called when the kernel module

    is loaded. Add the C code below to the end of the Csource file.

    i n t i n i t _ m o d u l e ( v o i d ) {

    s t r u c t b c m 2 8 3 5 _ p e r i p h e r a l * p = & m y c l o c k ;

    i n t s p e e d _ i d = 6 ; / * 1 f o r 1 9 M H z o r 6 f o r 5 0 0 M H z * /

    M a j o r = r e g i s t e r _ c h r d e v ( 0 , D E V I C E _ N A M E , & f o p s ) ;

    i f ( M a j o r < 0 ) {

    p r i n t k ( K E R N _ A L E R T " R e g . c h a r d e v f a i l % d \ n " , M a j o r ) ;

    r e t u r n M a j o r ;

    }

    p r i n t k ( K E R N _ I N F O " M a j o r n u m b e r % d . \ n " , M a j o r ) ;

    p r i n t k ( K E R N _ I N F O " c r e a t e d a d e v f i l e w i t h \ n " ) ;

    p r i n t k ( K E R N _ I N F O " ' m k n o d / d e v / % s c % d 0 ' . \ n " ,

    D E V I C E _ N A M E , M a j o r ) ;

    / * M a p G P I O * /

    i f ( m a p _ p e r i p h e r a l ( & g p i o ) = = - 1 ) {

    p r i n t k ( K E R N _ A L E R T " F a i l e d t o m a p t h e G P I O \ n " ) ;

    r e t u r n - 1 ;

    }

    / * D e f i n e i n p u t A D C c o n n e c t i o n s * /

    I N P _ G P I O ( B I T 0 _ P I N ) ;

    I N P _ G P I O ( B I T 1 _ P I N ) ;

    I N P _ G P I O ( B I T 2 _ P I N ) ;

    I N P _ G P I O ( B I T 3 _ P I N ) ;

    I N P _ G P I O ( B I T 4 _ P I N ) ;

    I N P _ G P I O ( B I T 5 _ P I N ) ;

    I N P _ G P I O ( B I T 0 _ P I N 2 ) ;

    I N P _ G P I O ( B I T 1 _ P I N 2 ) ;

    I N P _ G P I O ( B I T 2 _ P I N 2 ) ;

    I N P _ G P I O ( B I T 3 _ P I N 2 ) ;

    I N P _ G P I O ( B I T 4 _ P I N 2 ) ;

    I N P _ G P I O ( B I T 5 _ P I N 2 ) ;

    / * S e t a c l o c k s i g n a l o n P i n 4 * /

    p - > a d d r = ( u i n t 3 2 _ t * ) i o r e m a p ( C L O C K _ B A S E , 4 1 * 4 ) ;

    I N P _ G P I O ( 4 ) ;

    S E T _ G P I O _ A L T ( 4 , 0 ) ;

    * ( m y c l o c k . a d d r + 2 8 ) = 0 x 5 A 0 0 0 0 0 0 | s p e e d _ i d ;

    / * W a i t u n t i l c l o c k i s n o l o n g e r b u s y ( B U S Y f l a g ) * /

    w h i l e ( * ( m y c l o c k . a d d r + 2 8 ) & G Z _ C L K _ B U S Y ) { } ;

    / * S e t d i v i d e r t o d i v i d e b y 5 0 , t o r e a c h 1 0 M H z . * /

    * ( m y c l o c k . a d d r + 2 9 ) = 0 x 5 A 0 0 0 0 0 0 | ( 0 x 3 2 < < 1 2 ) | 0 ;

    / * T u r n t h e c l o c k o n * /

    * ( m y c l o c k . a d d r + 2 8 ) = 0 x 5 A 0 0 0 0 1 0 | s p e e d _ i d ;

    r e t u r n S U C C E S S ;

    }

    This function registers the new device (/ d e v / c h a r d e v

    ),maps the GPIO address and configures the inputconnections. It then sets the clock to run at 500MHz anduses a divider to provide a 10MHz clock signal on GPIOpin 4.

    The device file / d e v / c h a r d e v provides a mechanism foran external program to communicate with the kernelmodule. The 10MHz clock signal on GPIO Pin 4 is used todrive the ADC clock for sampling. More details on settingthe clock can be found in chapter 6.3 General PurposeGPIO Clocks in http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf

    The GPIO bit samples may not be synchronised with theclock. This can cause bits to be read from the samesample twice or be missed. This can be improved bysetting the clock as close as possible to the frequency ofthe GPIO readout.

    Clean up and device functions

    Add the C code below to the end of theS c o p e - d r v . c

    file.

    v o i d c l e a n u p _ m o d u l e ( v o i d ) {

    u n r e g i s t e r _ c h r d e v ( M a j o r , D E V I C E _ N A M E ) ;

    u n m a p _ p e r i p h e r a l ( & g p i o ) ;

    u n m a p _ p e r i p h e r a l ( & m y c l o c k ) ;

    }

    This function is called when the kernel module is unloaded.It removes the device file and unmaps the GPIO and clock.

    The implementation of four more functions need to beadded to the C file, to handle connections to the device fileassociated with the kernel module:

    s t a t i c i n t d e v i c e _ o p e n ( s t r u c t i n o d e * i n o d e ,

    s t r u c t f i l e * f i l e ) {

    s t a t i c i n t c o u n t e r = 0 ;

    i f ( D e v i c e _ O p e n ) r e t u r n - E B U S Y ;

    D e v i c e _ O p e n + + ;

    s p r i n t f ( m s g , " C a l l e d d e v i c e _ o p e n % d t i m e s \ n " , c o u n t e r + + ) ;

    m s g _ P t r = m s g ;

    r e a d S c o p e ( ) ; / * R e a d A D C s a m p l e s i n t o m e m o r y . * /

    t r y _ m o d u l e _ g e t ( T H I S _ M O D U L E ) ;

    r e t u r n S U C C E S S ;

    }

    s t a t i c i n t d e v i c e _ r e l e a s e ( s t r u c t i n o d e * i n o d e ,

    s t r u c t f i l e * f i l e ) {

    D e v i c e _ O p e n - - ; / * W e ' r e n o w r e a d y f o r o u r n e x t c a l l e r * /

    m o d u l e _ p u t ( T H I S _ M O D U L E ) ;

    r e t u r n 0 ;

    }

    s t a t i c s s i z e _ t d e v i c e _ r e a d ( s t r u c t f i l e * f i l p , c h a r * b u f f e r ,

    s i z e _ t l e n g t h , l o f f _ t * o f f s e t ) {

    i n t b y t e s _ r e a d = 0 ; / * T o c o u n t b y t e s r e a d . * /

    i f ( * m s g _ P t r = = 0 ) r e t u r n 0 ;

    / * P r o t e c t a g a i n s t g o i n g o u t s i d e t h e b u f f e r . * /

    w h i l e ( l e n g t h & & b u f _ p < S c o p e B u f f e r S t o p ) {

    i f ( 0 ! = p u t _ u s e r ( * ( b u f _ p + + ) , b u f f e r + + ) )

    p r i n t k ( K E R N _ I N F O " P r o b l e m w i t h c o p y \ n " ) ;

    l e n g t h - - ;

    b y t e s _ r e a d + + ;

    }

    r e t u r n b y t e s _ r e a d ;

    }

    Scope-drv.c (7/10)

    Scope-drv.c (8/10)

    Scope-drv.c (9/10)

    http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdfhttp://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdfhttp://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdfhttp://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
  • 7/23/2019 Build Oscilloscope

    5/58

    s t a t i c s s i z e _ t d e v i c e _ w r i t e ( s t r u c t f i l e * f i l p ,

    c o n s t c h a r * b u f f , s i z e _ t l e n , l o f f _ t * o f f ) {

    p r i n t k ( K E R N _ A L E R T " T h i s o p e r a t i o n i s n ' t s u p p o r t e d . \ n " ) ;

    r e t u r n - E I N V A L ;

    }

    The d e v i c e _ o p e n ( ) function is called when the devicefile associated with the kernel module is opened. Opening

    the device file causes the ADC to be read out 10,000times, where the results are saved in memory. Thed e v i c e _ r e l e a s e ( )

    function is called when the devicefile is closed. The

    d e v i c e _ r e a d ( ) function is called

    when a process reads from the device file. This functionreturns the measurements that were made when thedevice file was opened. The last function

    d e v i c e _ w r i t e ( ) is needed to handle the case when a

    process tries to write to the device file.

    Building and loading the module

    Create a Makefile in the same directory as theS c o p e -

    d r v . c file. Then add

    o b j - m + = S c o p e - d r v . o

    a l l :

    m a k e - C / l i b / m o d u l e s / $ ( s h e l l u n a m e - r ) / b u i l d \

    M = $ ( P W D ) m o d u l e s

    c l e a n :

    m a k e - C / l i b / m o d u l e s / $ ( s h e l l u n a m e - r ) / b u i l d \

    M = $ ( P W D ) c l e a n

    where the indents should be a single tab. (Moreinformation on Makefiles is given in Issue 7 of The MagPi.)

    The kernel module can now be compiled on a Raspberry Piby typing

    m a k e

    Once the module has been successfully compiled, load themodule by typing:

    s u d o i n s m o d . / S c o p e - d r v . k o

    Then assign the device file, by typing:

    s u d o m k n o d / d e v / c h a r d e v c 2 4 8 0

    Connecting the ADC

    Now that the kernel module has been described, an ADCis needed to provide the input data. For this article, aCA3306 ADC from Intersil was used. This is a 6-bit 15MSPS ADC with a parallel read out. This ADC is verycheap and fast. Many other ADC chips with parallelreadout could be used, although it is necessary to checkthe datasheet for connection details and clock speed

    settings, etc..

    For the selected ADC, 6-bit implies that between theground level (0V) and the reference voltage (5V) there are

    64 divisions to represent the signal. This is quite course,but is enough for simple applications.

    The selected ADC operates with 5V logic, but theRaspberry Pi uses 3V3 logic. Therefore, a level converteris needed to protect the Raspberry Pi from beingdamaged. The simplest way to achieve this is to use adedicated level converter, such as the TXB0108 fromTexas Instruments. To ensure that stable readings areobtained from the ADC, it is recommended that a separate5V supply is used as your VREF+ and VDD supply. Thisprevents voltage drops that can occur if the power supplyis shared with the Raspberry Pi. However, a commonground (GND) connection should be used for the external

    supply, ADC and Raspberry Pi.

    Data acquisition

    Once the ADC has been connected and the kernel modulehas been loaded, data can be read from the ADC byconnecting to the device file associated with the kernelmodule. To connect to the kernel module, anotherprogram is needed. This program could be written inseveral different programming languages. For this article,C++ was chosen. Create a new file called

    r e a d o u t . c p p

    and add the C++ given below and on the next page.

    # i n c l u d e < i o s t r e a m >

    # i n c l u d e < c m a t h >

    # i n c l u d e < f s t r e a m >

    # i n c l u d e < b i t s e t >

    t y p e d e f u n s i g n e d i n t u i n t 3 2 _ t ;

    / * T o m a t c h k e r n e l m o d u l e d a t a s t r u c t u r e * /

    c o n s t i n t D a t a P o i n t s R P i = 1 0 0 0 0 ;

    s t r u c t D a t a S t r u c t R P i {

    u i n t 3 2 _ t B u f f e r [ D a t a P o i n t s R P i ] ;

    u i n t 3 2 _ t t i m e ;

    } ;

    Scope-drv.c (10/10)