在前文已经成功移植好linux内核在stm32中稳定地运行,下面是要编写简单的驱动程序,先选择写最简单的led灯驱动。
led灯驱动归属于linux驱动的字符驱动这一类,而参考Uclinux源代码中usart设备驱动发现其采用的是平台设备驱动,不同于编写字符驱动。
于是找编写字符驱动的资料,这在网上一抓一大把,对于这个led灯驱动的底层关键是各种GPIO寄存器的设置,通过查阅stm32f407的数据手册可知,需要设置GPIOx_MODER、OTYPER、OSPEEDR、PUPDR这几个寄存器,注意到的是我现在用的是stm32f407,与网上大把部分的s3c2440寄存器设置是大不一样的,因为平台的不同,需要根据实际参照数据手册来进行你的设置。 我的板子只有一个led灯,输出gpio口为PC0,于是参照手册先设置GPIOC_MODER,查到寄存器地址为0x40020808,如下图:
直接取地址进行设置:
*#define GPIOC_MODER 0x40020800
((volatile unsigned )GPIOC_MODER) |= 0xffffff11;
其它寄存设置也是作类似的修改,以下是代码:
#ifndef __CONFIG_H
#define __CONFIG_H
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/io.h> /* writel */
#include <asm/atomic.h>
#include <asm/gpio.h>
#include <asm/uaccess.h>
#include <mach/iomux.h>
#include <mach/platform.h>
#include <mach/stm32.h>
#define GPIOC_MODER 0x40020800
#define GPIOC_OTYPER 0x40020804
#define GPIOC_OSPEEDR 0x40020808
#define GPIOC_PUPDR 0x4002080C
#define GPIOC_ODR 0x40020814
#define RCC_AHB1ENR 0x40023830
#define RCC_APB2ENR 0x40023844
#define LED_MAJOR_NR 231
#define DEVICE_NAME "led"
#define SET_LED_OFF 0
#define SET_LED_ON 1
static struct class *led_class;
static struct device *led_device;
#endif
static int led_open(struct inode *inode, struct file *filp);
static int led_release(struct inode *inode, struct file *filp);
static int led_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,unsigned long param);
int led_init(void);
void led_exit(void);
int led_open(struct inode *node, struct file *filp)
{
printk("open a led driver.\n");
(*(volatile unsigned *)RCC_AHB1ENR) |=0x00d00004;
(*(volatile unsigned *)RCC_APB2ENR) |=0x00000800;
(*(volatile unsigned *)GPIOC_MODER) |= 0xffffff11;
(*(volatile unsigned *)GPIOC_OTYPER) |= 0xffffff00;
(*(volatile unsigned *)GPIOC_OSPEEDR) |= 0xffffff33;
(*(volatile unsigned *)GPIOC_PUPDR) |= 0xffffff11;
return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
return(0);
}
static int led_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
if( arg > 2 )
return -1;
switch(cmd)
{
case 0:
(*(volatile unsigned *)GPIOC_ODR) |= 0x1 << arg;
break;
case 1:
(*(volatile unsigned *)GPIOC_ODR) &= 0x0 << arg;
break;
default:
return -1;
break;
}
return 0;
}
static struct file_operations led_fops =
{
.owner=THIS_MODULE,
.open = led_open,
.ioctl = led_ioctl,
.release=led_release
};
int led_init(void)
{
int result;
result = register_chrdev(231, "led", &led_fops);
#if defined(CONFIG_GPIOLIB) && defined(CONFIG_GPIO_SYSFS)
gpio_dsc.port = 2;
gpio_dsc.pin = 0;
stm32f2_gpio_config(&gpio_dsc, STM32F2_GPIO_ROLE_OUT);
if (result < 0)
{
printk(KERN_ERR DEVICE_NAME ": Unable to get major %d\n", LED_MAJOR_NR );
return(result);
}
printk("register_chrdev_region OK\n");
led_class = class_create(THIS_MODULE, "led");
led_device = device_create(led_class, NULL, MKDEV(231, 0), NULL, "led");
return(0);
}
void led_exit()
{
unregister_chrdev(231, "led");
device_unregister(led_device);
class_destroy(led_class);
}
module_init(led_init);
module_exit(led_exit);
MODULE_AUTHOR("dq");
MODULE_DESCRIPTION("STM32 led driver");
MODULE_LICENSE("GPL");
保存为led.c文件,存放在linux的/driver/char目录下,led.c文件中的关键部分是文件操作函数:fops。
我在编译busybox时进行了裁剪,没有insmod命令,因此我采用的是静态加载驱动编译进内核的方式:
1、修改linux/driver/char/makefile
加入obj-$(CONFIG_GPIO_STM32_LED)+= led.o
2、在同目录下的Kconfig文件
加
config GPIO_STM32_LED
bool “STM32-LED GPIO support”
3、在根文件系统dev/目录手工添加设备节点led
mknod -m 666 led c 231 0
4、编译ledtest.c,放入根文件系统目录,编译内核,运行应用程序ledtest。
敲指令ledtest on 灯亮
敲指令ledtest off 灯灭
应用程序ledtest.c如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
void delay(int delay)
{
int i;
for(;delay>0;delay--)
{
for(i=0 ; i < 5000 ; i ++);
}
}
int main(int argc ,char *argv[])
{
int fd1;
int j;
int val = 0;
fd1= open("/dev/led" , O_RDWR);
if(fd1 == -1)
{
printf ( "file can not be open.\n" );
return -1;
}
if (argc != 2)
{
printf("Usage:\n");
printf("%s <on|off>\n",argv[0]);
return 0;
}
if(strncmp(argv[1],"on",2) == 0)
{
val = 1;
}
else if (strncmp(argv[1],"off",3) == 0)
{
val = 0;
}
ioctl(fd1 , val , 0);
return 0;
}