Due dates Embedded Systems Lab 2. LED Driver Module Demonstration: 4 PM - 6 PM, Thu. Oct 16, 2014. Lab., Room 2353, N5 building. Report: 6 PM, Mon., Oct 27, 2014. RTCL, room 1230, E3-2 building. 1. Purpose Understand how to drive the GPIO LEDs in BeagleBone with a suitable module driver. Target board: BeagleBone (containing 4 GPIO LEDs) with Linux. Host computer: PC with Linux, cross compiler, NFS, and minicom. This experiment is for developing a device driver using the module concept, and to run on the module on the BeagleBone. 2. Problem Statement Problem 2 (LED Display) Write a module program for LED display output on the embedded board. Also write an application program named “metronome2” in the embedded board to output beats of the metronome visually using LED lamps with fixed tempo of 120 and fixed time-signature of 6/8. LED Output Beats (LED lamps on) with respect to time Period Given by Tempo input 120. Number of LED lamps 4. Controlled by Time signature input. (3 strong, 2 medium, 1 weak) Duty 50 % Number of LED lamp output Time signature 6/8: 3 1 1 2 1 1 ; 3 1 1 2 1 LED display pattern (‘O’ LED 4: X X X X LED 3: O X X X LED 2: O X X X LED 1: O X O X means on, ‘.’ X X X X X X X X X X X X O X X O X O X O means off): X X X X X X X O X X X O X O X O X X X X X X X O X X X X X X X O X X X X X X O O X X X X X X X O X X X X X X X O 1 ;… X X X X … … … … Note. The various user-configurable tempo and beat will be implemented in the future. 3. Design A. Hardware connection User I/O PC Serial/USB connection Embedded board GPIO LEDs (keyboard and display) 1 Fig. 2.1 Block Diagram for Lab 2 B. Software Connection You require three programs: 1) Minicom on the PC: The same as used in Lab 1. 2) Metronome2 on Embedded Board: Application program. 3) LED driver module on the Embedded Board: Module program. Metronome2 on embedded board should call a module program on embedded board in order to access LEDs. Minicom on the PC can be used for displaying messages from your application program. C. LED hardware circuit in BeagleBone [Refer Beaglebone System Reference Manual A5. p. 44] User LEDs Four user LEDS are provided via GPIO pins on the processor. Figure 19 below shows the LED circuitry. 2 Fig 2.2. User LEDS in BeagleBone Table 5. User LED Control LED GPIO User 0 GPIO1_21 User 1 GPIO1_22 User 2 GPIO1_23 User 3 GPIO1_24 D. GPIO (General Purpose I/O) Internal block diagram of AM3359 processor in BeangeBone is shown in Fig. 2.3. You can see “GPIO” under Parallel input/output blocks. 3 Fig. 2.3 Internal block diagram of AM3359 Processor. [Refer http://www.ti.com/product/am3359] Purpose of GPIO peripheral in AM335x processor The general-purpose interface combines four general-purpose input/output (GPIO) modules. Each GPIO module provides 32 dedicated general-purpose pins with input and output capabilities; thus, the general-purpose interface supports up to 128 (4 × 32) pins. These pins can be configured for the following applications: Data input (capture)/output (drive) Keyboard interface with a debounce cell Interrupt generation in active mode upon the detection of external events. Detected events are processed by two parallel independent interrupt-generation submodules to support biprocessor operations. GPIO Features Each GPIO module is made up of 32 identical channels. Each channel can be configured to be used in the following applications: Data input/output Keyboard interface with a de-bouncing cell Synchronous interrupt generation (in active mode) upon the detection of external events (signal transition(s) and/or signal level(s)) Wake-up request generation (in Idle mode) upon the detection of signal transition(s) Global features of the GPIO interface are: 4 Synchronous interrupt requests from each channel are processed by two identical interrupt generation sub-modules to be used independently by the ARM Subsystem Wake-up requests from input channels are merged together to issue one wake-up signal to the system Integration The device instantiates four GPIO_V2 modules. Each GPIO module provides the support for 32 dedicated pins with input and output configuration capabilities. Input signals can be used to generate interruptions and wake-up signal. Two Interrupt lines are available for bi-processor operation. Pins can be dedicated to be used as a keyboard controller. With four GPIO modules, the device allows for a maximum of 128 GPIO pins. (The exact number available varies as a function of the device configuration and pin muxing.) GPIO0 is in the Wakeup domain and may be used to wakeup the device via external sources. GPIO[1:3] are located in the peripheral domain. 5 General-Purpose Interface Basic Programming Model Set and Clear Instructions The GPIO module implements the set-and-clear protocol register update for the data output and interrupt enable registers. This protocol is an alternative to the atomic test and set operations and consists of writing operations at dedicated addresses (one address for setting bit[s] and one address for clearing bit[s]). The data to write is 1 at bit position(s) to clear (or to set) and 0 at unaffected bit(s). Registers can be accessed in two ways: Standard: Full register read and write operations at the primary register address Set and clear (recommended): Separate addresses are provided to set (and clear) bits in registers. Writing 1 at these addresses sets (or clears) the corresponding bit into the equivalent register; writing a 0 has no effect. Therefore, for these registers, three addresses are defined for one unique physical register. Reading these addresses has the same effect and returns the register value. Refer Technical Reference Manual, pp. 4513 – 4515 for further details. E. Device Driver Device Driver Concepts [4] One of the fundamental purposes of a device driver is to isolate the user's programs from ready access to critical kernel data structures and hardware devices. Furthermore, a wellwritten device driver hides the complexity and variability of the hardware device from the user. For example, a program that wants to write data to the hard disk need not care if the disk drive uses 512-byte or 1024-byte sectors. The user simply opens a file and issues a write command. The device driver handles the details and isolates the user from the complexities and perils of hardware device programming. The device driver provides a consistent user interface to a large variety of hardware devices. It provides the basis for the familiar UNIX/Linux convention that everything must be represented as a file. Loadable Modules Unlike some other operating systems, Linux has the capability to add and remove kernel components at runtime. Linux is structured as a monolithic kernel with a well-defined interface for adding and removing device driver modules dynamically after boot time. This feature not only adds flexibility to the user, but it has proven invaluable to the device driver development effort. Assuming that your device driver is reasonably well behaved, you can insert and remove the device driver from a running kernel at will during the development cycle instead of rebooting the kernel every time a change occurs. Loadable modules have particular importance to embedded systems. Loadable modules enhance field upgrade capabilities; the module itself can be updated in a live system without the need for a reboot. Modules can be stored on media other than the root (boot) device, which can be space constrained. Driver File System Operations [4] Recall that module_init() is used for module initialization (with ‘insmod’ command), and module_exit() is used for module exit (with ‘rmmod’ command). Now we need some methods to interface with our device driver from our application program. After the device driver is loaded into a live kernel, the first action we must take is to prepare the driver for subsequent operations. The open() method is used for this purpose. After the driver has been opened, we 6 need routines for reading and writing to the driver. A release() routine is provided to clean up after operations when complete (basically, a close call). Finally, a special system call ‘ioctl()’ is provided for nonstandard communication to the driver. Listing 2.1 hellodev1.c /* * */ File hellodev1.c A simple character device driver with file system operations #include <linux/module.h> /* Needed by all modules */ #include <linux/fs.h> # define HELLO_MAJOR 234 static int debug_enable = 0; module_param(debug_enable, int, 0); MODULE_PARM_DESC(debug_enable, "Enable module debug mode."); struct file_operations hellodev1_fops; static int hellodev1_open(struct inode *inode, struct file *file) { printk("hellodev1_open: successful\n"); return 0; } static int hellodev1_release(struct inode *inode, struct file *file) { printk("hellodev1_release: successful\n"); return 0; } static ssize_t hellodev1_read(struct file *file, char *buf, size_t count, loff_t *ptr) { printk("hellodev1_read: returning zero bytes\n"); return 0; } static ssize_t hellodev1_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { printk("hellodev1_write: accepting zero bytes\n"); return 0; } static long hellodev1_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { 7 printk("hellodev1_ioctl: cmd=%d, arg=%ld\n", cmd, arg); return 0; } static int __init hellodev1_init(void) { int ret; printk("Hellodev1 Init - debug mode is %s\n", debug_enable ? "enabled" : "disabled" ); ret = register_chrdev(HELLO_MAJOR, "hellodev", &hellodev1_fops); if (ret < 0) { printk("Error registering hellodev1 fops\n"); return ret; } printk("Hellodev1: registered successfully!\n"); /* Init processing here ... */ return 0; } static void __exit hellodev1_exit(void) { unregister_chrdev(HELLO_MAJOR, "hellodev"); printk("Goodbye, hellodev1\n"); } struct file_operations hellodev1_fops = { owner: THIS_MODULE, read: hellodev1_read, write: hellodev1_write, compat_ioctl: hellodev1_ioctl, Changed 2.6.36 open: hellodev1_open, release: hellodev1_release, }; // ioctl --> compat_ioctl module_init(hellodev1_init); module_exit(hellodev1_exit); MODULE_AUTHOR("Christoper Hallinan"); MODULE_DESCRIPTION("Hello Dev 1 Example"); MODULE_LICENSE("GPL"); This expanded device driver example includes many new lines. From the top, we've had to add a new kernel header file to get the definitions for the file system operations. We've also defined a major number for our device driver. Next we see definitions for four new functions, our open, close, read, and write methods. In keeping with good coding practices, we've adopted a consistent naming scheme that will not collide with any other subsystems in the kernel. Our new methods are called hellodev1_open(), hellodev1_release(), hellodev1_read(), and hellodev1_write(), respectively. For purposes of this simple exercise, they are do nothing functions that simply print a message to the kernel log subsystem. 8 Notice that we've also added a new function call ‘register_chrdev()’ to our hellodev1_init() routine. This line registers our device driver with the kernel. With that registration call, we pass a structure containing pointers to the required methods. The kernel uses this structure, of type struct file_operations, to bind our specific device functions with the appropriate requests from the file system. When an application opens a device represented by our device driver and requests a read() operation, the file system associates that generic read() request with our module's hellodev1_read() function. Device Nodes and mknod A device node is a special file type in Linux that represents a device. Virtually all Linux distributions keep device nodes in a common location (specified by the Filesystem Hierarchy Standard [6]), in a directory called /dev. A dedicated utility is used to create a device node on a file system. This utility is called mknod. An example of node creation is the best way to illustrate its functionality and the information it conveys. In keeping with our simple device driver example, let's create the proper device node to exercise it: $ mknod /dev/hello1 c 234 0 Application program Now that we have a skeletal device driver, we can load it and exercise it. Listing 2.2 is a simple user space application that exercises our device driver. Listing 2.2 Application program use_hellodev1.c /* File use_hellodev1.c #include #include #include #include #include #include */ <stdio.h> <stdlib.h> <sys/types.h> <sys/stat.h> <fcntl.h> <unistd.h> int main(int argc, char **argv) { /* Our file descriptor int fd; int rc = 0; char *rd_buf[16]; */ printf("%s: entered\n", argv[0]); // Open the device fd = open("/dev/hellodev1", O_RDWR); if (fd == -1) { perror("open failed"); rc = fd; exit(-1); } printf("%s: open successful\n", argv[0]); // Issue a read rc = read(fd, rd_buf, 0); 9 if (rc == -1) { perror("read failed"); close(fd); exit(-1); } printf("%s: read: returning %d bytes!\n", argv[0], rc); // Close device close(fd); return 0; } F. GPIO LED Module [Hardware Interfacing on the BeagleBone, http://www.nathandumont.com/node/250] Pin mux The BeagleBone has far more peripherals than pins, despite being a BGA package. To get around this it assigns different functionality to each pin based on software selections like most other modern microcontrollers. The BeagleBone actually has a more flexible arrangement than previous BeagleBoard variants, allowing run-time customisation of most of the I/O pins from a set of files under the /sys/ folder. If you take a look at the BeagleBone System Reference Manual starting on page 48 is a description of the I/O pins. There are up to 66 3.3V GPIO pins available with several I²C, SPI and UART interfaces as well as timers and PWM etc available as options instead of GPIO on certain pins. There are a group of ADC pins as well but be careful the ADCs are only 1.8V tolerant so watch how many volts you put across them! The initial connector pinout mentions the most likely pin mode for each pin, but on the following pages each pin has details of the up to 8 functions available on each pin. The pin name as far as the kernel is concerned is always the MODE0 function, which, frustratingly, isn't always the "Signal Name" given in the connector pinout. GPIO Pins There are a total of 66 GPIO pins available on the BeagleBone making it a very capable controller for a lot of things. Using them without writing a Kernel module relies on toggling individual pins one at a time via control files though, so don't plan on driving big wide parallel busses or anything without significant effort! GPIO Hardware Overview Refer AM335X Technical Reference Manual (in pdf form), Ch 25. General-Purpose Input/Output (p. 4505) Purpose of the Peripheral The general-purpose interface combines four general-purpose input/output (GPIO) modules. Each GPIO module provides 32 dedicated general-purpose pins with input and output capabilities; thus, the generalpurpose interface supports up to 128 (4 × 32) pins. These pins can be configured for the following applications: • Data input (capture)/output (drive) • Keyboard interface with a debounce cell • Interrupt generation in active mode upon the detection of external events. Detected events are processed by two parallel independent interrupt-generation submodules to support biprocessor operations. • Wake-up request generation (in Idle mode) upon the detection of signal transition(s) GPIO Features Shared registers can be accessed through “Set & Clear” protocol. The wake-up feature of the GPIO modules is only supported on GPIO0. Integration 10 See Figs in p. 4507, TRM Clocks GPIO module runs using two clocks: • The debouncing clock is used for the debouncing sub-module logic (without the corresponding configuration registers). This module can sample the input line and filters the input level using a programmed delay. • The interface clock provided by the peripheral bus (OCP compatible system interface). It is used through the entire GPIO module (except within the debouncing sub-module logic). It clocks the OCP interface and the internal logic. Clock gating features allow adapting the module power consumption to the activity. Set and Clear Instructions The GPIO module implements the set-and-clear protocol register update for the data output and interrupt enable registers. This protocol is an alternative to the atomic test and set operations and consists of writing operations at dedicated addresses (one address for setting bit[s] and one address for clearing bit[s]). The data to write is 1 at bit position(s) to clear (or to set) and 0 at unaffected bit(s). Registers can be accessed in two ways: • Standard: Full register read and write operations at the primary register address • Set and clear (recommended): Separate addresses are provided to set (and clear) bits in registers. Writing 1 at these addresses sets (or clears) the corresponding bit into the equivalent register; writing a 0 has no effect. Therefore, for these registers, three addresses are defined for one unique physical register. Reading these addresses has the same effect and returns the register value. Clear Data Output Register (GPIO_CLEARDATAOUT): • A write operation in the clear data output register clears the corresponding bit in the data output register when the written bit is 1; a written bit at 0 has no effect. • A read of the clear data output register returns the value of the data output register. Set Data Output Register (GPIO_SETDATAOUT): • A write operation in the set data output register sets the corresponding bit in the data output register when the written bit is 1; a written bit at 0 has no effect. • A read of the set data output register returns the value of the data output register. Data Input (Capture)/Output (Drive) The output enable register (GPIO_OE) controls the output/input capability for each pin. At reset, all the GPIO-related pins are configured as input and output capabilities are disabled. This register is not used within the module; its only function is to carry the pads configuration. When configured as an output (the desired bit reset in GPIO_OE), the value of the corresponding bit in the GPIO_DATAOUT register is driven on the corresponding GPIO pin. Data is written to the data output register synchronously with the interface clock. This register can be accessed with read/write operations or by using the alternate set and clear protocol register update feature. This feature lets you set or clear specific bits of this register with a single write access to the set data output register (GPIO_SETDATAOUT) or to the clear data output register (GPIO_CLEARDATAOUT) address. If the application uses a pin as an output and does not want interrupt generation from this pin, the application must properly configure the interrupt enable registers. When configured as an input (the desired bit set to 1 in GPIO_OE), the state of the input can be read from the corresponding bit in the GPIO_DATAIN register. The input data is sampled synchronously with the interface clock and then captured in the data input register synchronously with the interface clock. When the GPIO pin levels change, they are captured into this register after two interface clock cycles (the required cycles to synchronize and to write data). If the application uses a pin as an input, the application must properly configure the interrupt enable registers to the interrupt as needed. 11 Device Memory Read/Write http://linuxgazette.net/issue83/thangaraju.html A device driver is an entry point to access a device. Developing a device driver is not as simple a task as writing application programs. Since any dynamically-loaded driver module is attached to the existing kernel, any error in the driver will crash the entire system. Resource allocation for a device is one of the main concerns for device driver developers. The device resources are I/O memory, IRQs and ports. This article presents a risk-free way of allocating resource for an I/O memory mapped device for a dynamically loaded Linux device driver, and is written so that less experienced Linux users can follow along. To register the given I/O memory regions, the macro is void request_mem_region (unsigned long start, unsigned long length, char *device_name); The string argument char *device_name is the name of device, which will own the I/O memory regions from start address to length size. Before the device is unregistered, the allocated I/O memory regions should be released for other devices. void release_mem_region (unsigned long start, unsigned long length); The above function will de-allocate the I/O memory regions. void* ioremap ( unsigned long phys_addr, unsigned long size ) Remap I/O memory into kernel address space. Here no real mapping is done. Only the virtual address is returned. To read from I/O memory, use: unsigned int ioread32(void *addr); Here, addr should be an address obtained from ioremap (perhaps with an integer offset); the return value is what was read from the given I/O memory. There is a similar set of functions for writing to I/O memory: void iowrite32(u32 value, void *addr); If you must read or write a series of values to a given I/O memory address, you can use the repeating versions of the functions: void ioread32_rep(void *addr, void *buf, unsigned long count); void iowrite32_rep(void *addr, const void *buf, unsigned long count); G. Plan for Files Obviously, to run an application with GPIO LED, you need two programs: Application program and LED device driver module program. It is very difficult if two programs – application and module – have bugs, since bugs interact together to generate very strange behavior. For this purpose, we need two step approaches: Debug module M with a simple application A1. Debug main application A2 with debugged module M. Hence we plan three files M, A1, and A2. 12 Module gpio_led_module1.c M This module should be usable for both A1 and A2. Hence M should include gpio_write(fp, data, size_of_1) The ‘data’ is encoded with binary number (ranging 0 to 15), instead of four integer array. In ‘data’, each bit n represents on/off of each LED n. We can write four user LEDs at one time! Include suitable header files from the kernel source. A simple application program A1 named gpio_led_app.c Application program to write to gpio_led_module1.ko. Iteration of get user input (using scanf with ‘%d’) and write() (actually gpio_write()). Negative user input terminates the program. Main application program A2 named metronome2.c Repeat displaying a measure 16 times. (Actually, infinite times, but we need to stop!) One measure contains time-signature of 6/8. H. Algorithm A typical algorithm can be constructed as follows. Module program M for LED display (gpio_led_module1.c) Open_GPIO_LED: Increase module use count. Write_GPIO_LED(data): Display the lower 4 bits of data using four LEDs. Close GPIO_LED: Decrease module use count. Init_module() Save the current GPIO configuration. Register the device using register_chrdrv() // Define file operations of open, write, and close. Initialize GPIO_IO_init() // Define usage of GPIO pins. Cleanup_module() Unregister the device using unregister_chrdrv(). Restore the GPIO configuration. A simple application program A1 for write to LEDs (gpio_led_app.c) Loop Get user input of an integer (using scanf with “%d” format) If the user input is negative, exit Otherwise, bound the user input to 15 max (4-bit binary) Call write() (actually to gpio_write()) to write to four LEDs at the same time. A main application program A2 for Metronome time-signature (metronome2.c) Set P = [ 7, 1, 1, 3, 1, 1]; Open_GPIO_LED // LED bit pattern to display 3, 2, or 1 lamps 13 Let Tempo = 120. Let Time_signature = 6/8. // You may use two variables: numerator and denominator Repeat the following 16 times (i.e., 16 measures) Repeat the following 6 times (i=0, … , 5) Write_GPIO_LED(P(i)) // Turn LED with pattern P(i) Sleep n ms // Depending on Tempo Write_GPIO_LED(0) // Turn all LEDs off Sleep n ms // Depending on Tempo End repeat i End repeat Close GPIO_LED Note that most of functions (LED pattern generation and timing, etc.) are performed in the application program A2. The timing and LED pattern can be implemented in the module program: This is better approach, and will be implemented in the succeeding labs. 4. Preparation A. Design the algorithm (flowchart) for M, A1, and A2 of the problem 2. Improve if required. B. Program the module program M - gpio_led_moduel1.c. Modification of the template GPIO driver in the Appendix is quite a good way. C. Program a simple application A1 - gpio_led_app.c. D. Program the main application program A2 – metronome2.c. 5. Lab Procedures Contents Step 1. Setup cross development environment for module Step 2. Test LEDs with commands Step 3. Compile simple driver module program Step 4. Run gpio_led_app app with GPIO LED driver module program Step 5. Run metronome2 app with GPIO LED driver module program. 14 Step 1. Setup cross development environment for module Note. Step 1 is already done in Lab 1, but should be repeated also after each power on. 1.1 Connection PC --- Ethernet cable --- Router – Ethernet cable --- Beaglebone PC ------------------------ USB cable --------------------- Beaglebone 1.2 Set the cross-compile environment for modules on PC $ cd ~/Embedded/sh $ source bu-cce-3813-bone63 Setting cross-development environment for BeagleBone Ubuntu 14.04... Done. 1.3 Start PC NFS server To start the NFS server, you can run the following command at a terminal prompt: $ sudo /etc/init.d/nfs-kernel-server start Check if nfs is started. $ ps -aux | grep nfs …... root 3005 0.0 0.0 0 0? S 17:51 0:00 [nfsd] Or simply $ ~/Embedded/sh/start_nfs_server.sh 1.4 Start nfs client on Beaglebone Log in to Beaglebone with id ‘ubuntu’ and passwd ‘temppwd’. Start nfs-client # su # ~/start_nfs_client.sh Step 2. Test LEDs using sysfs and commands 2.1 Select LEDs for test Note that four LEDs on BeagleBone are connected to GPIOs as follows: Table 5. User LED Control [from Beaglebone System Reference Manual] LED GPIO User 0 GPIO1_21 User 1 GPIO1_22 User 2 GPIO1_23 User 3 GPIO1_24 Selection: Test User 0 LED. 15 Since LED0 is used as heart beat signal for Ubuntu, we are going to stop the heartbeat, and perform our experiment. 2.2 Check sysfs file The control files are all contained in Beaglebone file system: # ls -F /sys/class/gpio export gpiochip0@ gpiochip32@ gpiochip64@ gpiochip96@ unexport Browse the contents of gpiochip32 (containing GPIO1_0 to 31) # cd /sys/class/gpio/gpiochip32 # ls -F base label ngpio power/ subsystem@ uevent Note # ls -F /sys/class/gpio/gpiochip32 /sys/class/gpio/gpiochip32@ # ls -la /sys/class/gpio/gpiochip32 lrwxrwxrwx 1 root root 0 Jan 1 2000 /sys/class/gpio/gpiochip32 -> ../../devices/virtual/gpio/gpiochip32 # ls -F /sys/devices/virtual/gpio/gpiochip32 base label ngpio power/ subsystem@ uevent Hence the actual location of gpiochip32 is /sys/devices/virtual/gpio/gpiochip32 . Getting access to a GPIO (GPIO1_21: User LED0) Request GPIO1_21 (GPIO53 for User LED 0) from the Kernel. # echo 53 > /sys/class/gpio/export -bash: echo: write error: Device or resource busy Oops! We have to find another way. 2.3 Find LED sysfs Search /sys/class: # ls -F /sys/class backlight/ hwmon/ mmc_host/ scsi_disk/ uio/ bdi/ i2c-adapter/ mtd/ scsi_host/ usbmon/ block/ i2c-dev/ net/ sound/ vc/ bsg/ input/ power_supply/ spidev/ video4linux/ dma/ lcd/ pps/ spi_master/ virtio-ports/ drm/ leds/ pwm/ thermal/ vtconsole/ dvb/ mbox/ rc/ timed_output/ watchdog/ firmware/ mdio_bus/ regulator/ tty/ gpio/ mem/ rtc/ ubi/ graphics/ misc/ scsi_device/ udc/ Found “leds”! Search /sys/class/leds: # ls -F /sys/class/leds beaglebone:green:usr0@ beaglebone:green:usr2@ 16 beaglebone:green:usr1@ beaglebone:green:usr3@ Here you see the directories for controlling each of the user LEDs. By default, usr0 flashes a heartbeat pattern and usr1 flashes when the micro SD card is accessed. Let's control usr0. 2.4 Get access right to usr0 LED Go to the directory /sys/class/leds # cd /sys/class/leds # cd beaglebone₩:green₩:usr0 Note that '₩' should be included before each ':'. # ls -F brightness device@ max_brightness power/ subsystem@ trigger uevent See what's in trigger # cat trigger none nand-disk mmc0 timer oneshot [heartbeat] backlight gpio cpu0 default-on tra nsient This shows trigger can have many values. The present value is heartbeat (enclosed with '[]'). Check the LED, is it beating? You can stop the heartbeat via: # echo none > trigger Heartbeat is stopped! Check: # cat trigger [none] nand-disk mmc0 timer oneshot heartbeat backlight gpio cpu0 default-on transient 2.5 Control on/off of usr0 LED Turn on/off usr0 LED # echo 1 > brightness # echo 0 > brightness Usr0 LED is turned on and off! 2.6 Control periodic on/off of usr0 LED LED trigger with timer and 10% duty: # echo timer > trigger # echo 100 > delay_on # echo 900 > delay_off Observe period and duty of User LED0. Step 3. Compile simple driver module program Note. In this Step, we test a simple device driver hellodev1.ko. 3.1 Make a working directory 17 $ mkdir –p ~/Embedded/lab2/a_hellodev $ cd ~/Embedded/lab2/a_hellodev 3.2 Edit hellodev1.c in Listing 2.1. 3.3 Edit Makefile and make Edit Makefile $ gedit Makefile Makefile contents: # Embedded Bone-Ubuntu cross-compile module makefile ifneq ($(KERNELRELEASE),) obj-m := hellodev1.o else SUBDIRS := $(shell pwd) default: ifeq ($(strip $(KERNELDIR)),) $(error "KERNELDIR is undefined!") else $(MAKE) -C $(KERNELDIR) M=$(SUBDIRS) modules endif app: arm-linux-gnueabihf-gcc -o use_hellodev1 use_hellodev1.c clean: rm -rf *~ *.ko *.o *.mod.c modules.order Module.symvers .pwm* .tmp_versions rm use_hellodev1 endif Make $ make Check that hellodev1.ko module is generated. 3.4 Edit application program Edit app use_hellodev1.c in Listing 2.2. $ gedit use_hellodev1.c 3.5 Make application $ make app Check that use_hellodev1 program is generated. 3.6 Test on Beaglebone using nfs. Insert module hellodev1.ko # insmod hellodev1.ko 18 # dmesg | tail …... [ 5105.054193] Hellodev1 Init - debug mode is disabled [ 5105.054253] Hellodev1: registered successfully! # lsmod Hellodev1.ko …… Run app. # ./use_hellodev1 ./use_hellodev1: entered open failed: No such file or directory Oops! Make node. # mknod /dev/hellodev1 c 234 0 Run app again # ./use_hellodev1 ./use_hellodev1: entered ./use_hellodev1: open successful ./use_hellodev1: read: returning 0 bytes! # dmesg | tail …... [ 5357.101638] hellodev1_open: successful [ 5357.101772] hellodev1_read: returning zero bytes [ 5357.101903] hellodev1_release: successful Remove module # rmmod hellodev1 Step 4. Run gpio_led_app app with GPIO_LED driver module program 4.1 Make a working directory Make a suitable working directory, such as lab2/e_gpio_led_module1 $ mkdir ~/Embedded/lab2/e_gpio_led_module1 $ cd ~/Embedded/lab2/e_gpio_led_module1 4.2 Edit prepared gpio_led_module1.c $ gedit gpio_led_module1.c Make sure that you set correct header files: am33xx.h and gpio.h in the directory ~/Embedded/Include. 4.3. Make 19 Edit prepared Makefile $ gedit Makefile Make $ Make 4.4 Edit prepared app – gpio_led_app.c $ gedit gpio_led_app.c 4.5 Make app. $ make app Check that gpio_led_app program is generated. 4.6 Test on Beaglebone. Insert module # insmod gpio_led_module1.ko Note the Major number reported: N. # dmesg | tail [ 1054.200144] A. Init gpio_led: The major number is 240 In this case, N equals 240. Make node # mknod /dev/gpio_led C 240 0 Run app. # ./gpio_led_app GPIO_LED device open success Enter data for LED: 15 Write GPIO-LED with data f Enter data for LED: -9 GPIO_LED closed. // All four LEDs on! Step 5. Run metronome2 app with GPIO_LED driver module program. 5.1 Use the same directory as in Step 4. 5.2 Edit prepared app – metronome2.c $ gedit metronome2.c 5.3 Make app. Edit the Makefile suitably. Make $ make app Check that metronome2 program is generated. 20 5.4 Test on Beaglebone. # insmod gpio_led_module1.ko # dmesg | tail # mknod /dev/gpio_led C 240 0 # ./metronome2 6. Demonstration Demonstrate the result of Step 5.4 to TA. 7. Report Each student should prepare his own report containing: Purpose Experiment sequence Experimental results Discussion: should be different even for each member of the same team. References 8. References [1] BeagleBone A5 System Reference manual. [2] AM335x ARM Cortex-A8 Microprocessors (MPUs) Technical Reference Manual (Rev. F), Texas Instruments, http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf [3] AM335x ARM Cortex-A8 Microprocessors (MPUs) (Rev. D) , Texas Instruments, http://www.ti.com/lit/ds/symlink/am3359.pdf [4] Christopher Hallinan, “Embedded Linux Primer”, Prentice Hall, 2007. Appendix Template program gpio_led_module1.c // File template_gpio_led_module1.c // // A simple character driver with four user LEDs in Beaglebone Ubuntu // // Programmed by Byung Kook Kim, Sep. 26, 2014. #include #include #include #include <linux/module.h> <linux/fs.h> <linux/ioport.h> <asm/io.h> #include #include #include #include <linux/init.h> <linux/device.h> <linux/semaphore.h> <linux/cdev.h> 21 #include #include #include #include #include #include #include <linux/fs.h> <linux/errno.h> <asm/uaccess.h> <linux/moduleparam.h> <linux/string.h> <linux/ioctl.h> <linux/slab.h> // AM33xx specific register definitions //- #include <plat/am33xx.h> //-#include <plat/gpio.h> #include "../../Include/am33xx.h" #include "../../Include/gpio.h" ///#define GPIO_LED_MAJOR 234 #define GPIO_LED_MAJOR 0 // Should match to mknod // Auto select major number // GPIO_LED File operations table struct file_operations gpio_led_fops; // GPIO1 usage flag static int gpio1_usage = 0; int major; // Device major number in init/cleamup_module // Global data // GPIO1 physical/virtual address unsigned long gpio1_start = AM33XX_GPIO1_BASE; // GPIO1 Start address unsigned long gpio1_len = 0x1000; // Length of GPIO1 addresses void __iomem * gpio1_vbase = NULL; // GPIO1 base virtual address int gpio1_chk; // You need to insert your codes into this function. static ssize_t gpio_write (struct file *filp, const char *wbuf, size_t wcount, loff_t *f_pos) { unsigned int mdata; unsigned int oe1, oe1new; unsigned int dout1, dout1new; // Get data from app program get_user(mdata, wbuf); // (To var, Src addr) // First set GPIO1 21 - 25 direction to output ('0') // Add oyur code: Read GPIO1 Direction register to oe1 // Add your code: Set new value of GPIO1 Direction register as oe1new. // Add your code: Write GPIO1 Direction register printk("GPIO1_OE: %08x to %08x\n", oe1, oe1new); // // // // Then set Add oyur Add oyur Add oyur gpio data out code: Set GPIO1 Clear Register code: Set GPIO1 Set Register code: printk some message return (0); } 22 // GPIO open function for open() call static int gpio_open(struct inode *inode, struct file *filp) { int result; // 0. Check & set GPIO1 usage flag if( gpio1_usage != 0 ) return -EBUSY; gpio1_usage = 1; // B. memory mapping for GPIO control // B1. Check memory region and then request - GPIO1 gpio1_chk = check_mem_region(gpio1_start, gpio1_len); if (gpio1_chk < 0) { printk(" B1. Warning: GPIO1 check_mem_region failed...\n"); //- return(result); } else { request_mem_region (gpio1_start, gpio1_len, "gpio1"); // printk(" B1. GPIO1 request_mem_region.\n"); } // B2. Physical to virtual memory mapping - GPIO1 gpio1_vbase = ioremap(gpio1_start, gpio1_len); if (!gpio1_vbase) { printk(" B2. GPIO1 ioremap failed.\n"); // -B1. Release memory region if (gpio1_chk >= 0) release_mem_region (gpio1_start, gpio1_len); return(-2); } // printk(" B2. gpio_led open: ioremap gpio1_vbase= %08x\n", (unsigned int)gpio1_vbase); // C. Set MUXes // Assumed to be done by Kernel correctly. printk(" D. gpio_led opened.\n"); return 0; } // GPIO release function for release() call static int gpio_release(struct inode *inode, struct file *filp) { // -B. REverse handling GPIO1 memory map // -B2. Unmap ioaddress iounmap(gpio1_vbase); // -B1. Release memory region if (gpio1_chk >= 0) release_mem_region (gpio1_start, gpio1_len); // -0. Clear GPIO1 usage flag gpio1_usage = 0; printk(" -D. gpio_led released.\n"); 23 return 0; } // Define file operations table for gpio_led driver struct file_operations gpio_led_fops = { owner: THIS_MODULE, open: gpio_open, write: gpio_write, release: gpio_release, }; // GPIO_LED init_module static int __init gpio_led_init_module (void) { // A. Register gpio_led as a character device major = register_chrdev( GPIO_LED_MAJOR, "gpio_led", &gpio_led_fops ); if (major < 0) { printk(KERN_WARNING "Error registering gpio_led fops %d\n", major); return major; } printk(" A. Init gpio_led: The major number is %d\n", major); // Return success return 0; } // GPIO_LED cleanup_module static void __exit gpio_led_cleanup_module (void) { // -A. Unregister gpio_led device unregister_chrdev( GPIO_LED_MAJOR, "gpio_led" ); printk(" -A. Cleanup gpio_led: Unregistered.\n"); } // Define module init and cleanup functions module_init(gpio_led_init_module); module_exit(gpio_led_cleanup_module); MODULE_AUTHOR("Byung Kook Kim"); MODULE_DESCRIPTION("GPIO_LED Driver"); MODULE_LICENSE("GPL"); FIN 24
© Copyright 2024