作业要求:
编写led驱动,通过应用程序控制三盏灯亮灭
作业答案:
作业效果:
mychrdev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include "head.h"
unsigned int major; // 保存申请的设备号
char kbuf[128] = {0}; // 用于保存向内核中传输的消息
// 用来接收映射成功的内存首地址
unsigned int *moder_e;
unsigned int *odr_e;
unsigned int *moder_f;
unsigned int *odr_f;
unsigned int *rcc;
/*
// 延时函数
void delay(int ms)
{
int i, j;
for (i = 0; i < ms; i++)
{
for (j = 0; j < 2000; j++)
{
}
}
}
*/
// 封装常用的操作方法
int mychrdev_open(struct inode *inode, struct file *file)
{
printk("调用mychrdev_open成功,%d\n", __LINE__);
return 0;
}
ssize_t mychrdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *lof)
{
printk("调用成功mychrdev_read,%d\n", __LINE__);
// 用户调用了read后会调用这个函数
// 所以是从内核空间向用户空间写
unsigned int ret; // 用于接收返回值,判断是否成功
ret = copy_to_user(ubuf, kbuf, size);
if (ret)
{
printk("数据拷贝失败\n");
return -ret;
}
return 0;
}
ssize_t mychrdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *lof)
{
printk("调用mychrdev_write成功,%d\n", __LINE__);
// 用户调用了write后会调用这个函数
// 所以是从用户空间向内核空间写
unsigned int ret; // 用于接收返回值,判断是否成功
ret = copy_from_user(kbuf, ubuf, size);
if (ret)
{
printk("数据拷贝失败\n");
return -ret;
}
if (kbuf[0] == '0') // 开1号灯
{
(*odr_e) |= (0x1 << 10);
}
else if (kbuf[0] == '1') // 开2号灯
{
(*odr_f) |= (0x1 << 10);
}
else if (kbuf[0] == '2') // 开3号灯
{
(*odr_e) |= (0x1 << 8);
}
else if (kbuf[0] == '3') // 开三个灯
{
(*odr_e) |= (0x1 << 10);
(*odr_f) |= (0x1 << 10);
(*odr_e) |= (0x1 << 8);
}
else if (kbuf[0] == '4') // 关1号灯
{
(*odr_e) &= (~(0x1 << 10));
}
else if (kbuf[0] == '5') // 关2号灯
{
(*odr_f) &= (~(0x1 << 10));
}
else if (kbuf[0] == '6') // 关3号灯
{
(*odr_e) &= (~(0x1 << 8));
}
else if (kbuf[0] == '7') // 关三个灯
{
(*odr_e) &= (~(0x1 << 10));
(*odr_f) &= (~(0x1 << 10));
(*odr_e) &= (~(0x1 << 8));
}
return 0;
}
int mychrdev_close(struct inode *inode, struct file *file)
{
printk("调用mychrdev_close成功,%d\n", __LINE__);
return 0;
}
// 定义操作方法指针
struct file_operations fops = {
.open = mychrdev_open,
.read = mychrdev_read,
.write = mychrdev_write,
.release = mychrdev_close};
static int __init mycdev_init(void)
{
// 注册字符设备驱动
// 填0代表动态申请主设备号
major = register_chrdev(0, "mychrdev", &fops);
// 申请失败
if (major < 0)
{
printk("注册字符设备驱动失败 %d\n", __LINE__);
}
printk("注册字符设备驱动成功 major = %d\n", major);
// 进行LED寄存器的地址映射
moder_e = ioremap(GPIOE_MODER, 4);
if (moder_e == NULL)
{
printk("地址映射失败%d\n", __LINE__);
return 0;
}
moder_f = ioremap(GPIOF_MODER, 4);
if (moder_f == NULL)
{
printk("地址映射失败%d\n", __LINE__);
return 0;
}
odr_e = ioremap(GPIOE_ODR, 4);
if (odr_e == NULL)
{
printk("地址映射失败%d\n", __LINE__);
return 0;
}
odr_f = ioremap(GPIOF_ODR, 4);
if (odr_f == NULL)
{
printk("地址映射失败%d\n", __LINE__);
return 0;
}
rcc = ioremap(RCC_GPIO, 4);
if (rcc == NULL)
{
printk("地址映射失败%d\n", __LINE__);
return 0;
}
printk("地址映射成功\n");
// 硬件初始化
// 使能时钟
(*rcc) |= (0X3 << 4);
// 设置PE10、PF10、PE8为输出
(*moder_e) &= (~(0X3 << 20));
(*moder_e) |= (0X1 << 20);
(*moder_f) &= (~(0X3 << 20));
(*moder_f) |= (0X1 << 20);
(*moder_e) &= (~(0X3 << 16));
(*moder_e) |= (0X1 << 16);
// 默认输出低电平
(*odr_e) &= (~(0x1 << 10));
(*odr_f) &= (~(0x1 << 10));
(*odr_e) &= (~(0x1 << 8));
return 0;
}
static void __exit mycdev_exit(void)
{
// 取消内存映射
iounmap(moder_e);
iounmap(odr_e);
iounmap(moder_f);
iounmap(odr_f);
iounmap(rcc);
// 注销字符设备驱动
unregister_chrdev(major, "mychrdev");
printk("注销字符设备驱动成功 %d\n", __LINE__);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
led.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,const char * argv[])
{
//用于接收读取到的数据
char buf[128] = {};
//打开自定义字符设备文件
int fd = open("/dev/mychrdev",O_RDWR);
//当打开失败时
if(fd < 0)
{
printf("打开自定义字符设备文件失败\n");
return -1;
}
//开关灯的逻辑控制
while (1)
{
printf("请输入控制码:0(开1号灯) 1(开2号灯) 2(开3号灯) \
3(开三个灯) 4(关1号灯) 5(关2号灯) \
6(关3号灯) 7(关三个灯)>");
fgets(buf, sizeof(buf), stdin); // 从终端输入一个数据
buf[strlen(buf) - 1] = '\0';
write(fd, buf, sizeof(buf));
}
//关闭
close(fd);
return 0;
}
head.h
#ifndef __HEAD_H__
#define __HEAD_H__
#define GPIOE_MODER 0X50006000
#define GPIOF_MODER 0X50007000
#define GPIOE_ODR 0X50006014
#define GPIOF_ODR 0X50007014
#define RCC_GPIO 0X50000A28
#endif