编写基于stm32f407-uclinux下的led灯驱动

时间:2021-06-26 23:35:50

       在前文已经成功移植好linux内核在stm32中稳定地运行,下面是要编写简单的驱动程序,先选择写最简单的led灯驱动。

       led灯驱动归属于linux驱动的字符驱动这一类,而参考Uclinux源代码中usart设备驱动发现其采用的是平台设备驱动,不同于编写字符驱动。

       于是找编写字符驱动的资料,这在网上一抓一大把,对于这个led灯驱动的底层关键是各种GPIO寄存器的设置,通过查阅stm32f407的数据手册可知,需要设置GPIOx_MODER、OTYPER、OSPEEDR、PUPDR这几个寄存器,注意到的是我现在用的是stm32f407,与网上大把部分的s3c2440寄存器设置是大不一样的,因为平台的不同,需要根据实际参照数据手册来进行你的设置。 我的板子只有一个led灯,输出gpio口为PC0,于是参照手册先设置GPIOC_MODER,查到寄存器地址为0x40020808,如下图:


编写基于stm32f407-uclinux下的led灯驱动

       直接取地址进行设置:

*#define GPIOC_MODER 0x40020800

((volatile unsigned )GPIOC_MODER) |= 0xffffff11;

其它寄存设置也是作类似的修改,以下是代码:

/*

* Simple character interface to GPIOs. Allows you to read/write values and

* control the direction. Maybe add wakeup when gpio framework supports it ...

*

* based on the work of "Mike Frysinger <vapier@gentoo.org>"

*

* Adapted to drive the STM32 MCU, GPIO Port F PINs

*

* Copyright 2008 Analog Devices Inc.

* Licensed under the GPL-2 or later.

*/




#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>









/*configue for stm32f407 GPio C*/

#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



//struct cdev led_dev;

//dev_t devno;

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; //open gpioc clock

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

(*(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); //register char device



#if defined(CONFIG_GPIOLIB) && defined(CONFIG_GPIO_SYSFS)



/* PC0 = LED LD3 */

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"); //create led_class



led_device = device_create(led_class, NULL, MKDEV(231, 0), NULL, "led"); //create led device



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”

在编译内核时,make menuconfig中的Device中选上添加的选项

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;

}