精华内容
下载资源
问答
  • STM32之点亮LED灯程序

    2020-08-11 07:36:34
    这是一个STM32之点亮LED灯程序,含有配置图及相关原理图,简单易懂。
  • 本文主要为51单片机串口通信点亮led灯程序,下面一起来学习下
  • PIC12F629点亮LED灯测试程序,采用内部内部RC,给初学入门者参考使用
  • LED台灯电路及程序
  • STM32点灯程序合集1:新建固件库工程文件1.1找到一个固件库模板1.2:打开KEIL5新建工程1.3:添加相关文件1.4:手动添加其他文件路径进来2:STM32固件库点灯2.1: 方式1点灯main.c2.2 :方式2点灯main.c2.3 :方式3点灯...

    今天学习了STM32固件库点灯程序 点灯也可以玩过很多好玩的东西,在这里做一下总结。

    1:新建固件库工程文件

    1.1找到一个固件库模板

    在这里插入图片描述

    1.2:打开KEIL5新建工程

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    最终效果:
    在这里插入图片描述

    1.3:添加相关文件

    然后我们针对不同文件夹 我们添加不同的文件进来。
    双击STARTUP 添加我们固件库的启动文件

    STARTUP:添加Libraries——>CMSIS——>startup中的启动文件
    在这里插入图片描述

    USER:添加固件库的User文件夹中的.c文件

    CMSIS 添加Libraries——>CMSIS文件夹中的.c文件
    DOC:添加固件库中Doc文件夹的readme.txt
    FWLIB:添加Libraries——>STM32F10x_StdPeriph_Driver文件夹中的.c文件

    添加完效果
    在这里插入图片描述

    1.4:手动添加其他文件路径进来

    打开stm32f0x.h文件
    在这里插入图片描述

    按 Ctrl + F 查找 stm32f10x_conf

    会调到一下的代码 我们得宏定义加载一下
    在这里插入图片描述
    在这里插入图片描述
    添加其他文件夹.h路径
    在这里插入图片描述
    这样就是新建固件库工程文件完成了。。。。。
    下面就开始点灯 点灯 点灯
    下面就开始点灯 点灯 点灯
    下面就开始点灯 点灯 点灯

    2:STM32固件库点灯

    首先你得有个固件库手册来参考 我这里直接上代码。
    在这里插入图片描述
    我这个用的STM32C8T6板子 PC13是LED灯 根据原理图 给个低电平则点亮

    LED配置代码
    led.c

    #include "stm32f10x.h"
    #include "led.h"
    
    void LED_Init(void)
    {	
    	//1.打开控制GPIOC的时钟(APB2)
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    	
    	//2.配置结构体	
    	GPIO_InitTypeDef led_init;
    	led_init.GPIO_Pin   = GPIO_Pin_13;      //GPIOC13引脚
    	led_init.GPIO_Mode  = GPIO_Mode_Out_PP; //推挽输出	
    	led_init.GPIO_Speed = GPIO_Speed_10MHz; //10MHz
    	
    	//3.对成员进行初始化
    	GPIO_Init(GPIOC, &led_init);
    }
    
    

    led.h

    #ifndef _LED_H_
    #define _LED_H_
    
    #include "stm32f10x.h"
    
    void LED_Init(void); //LED初始化函数声明
    
    #endif
    

    2.1: 方式1点灯

    main.c

    #include "stm32f10x.h"
    #include "led.h"
    
    int  main()
    {
    	LED_Init(); //LED初始化
    	
    	while(1)
    	{
    		//方式1
    		//GPIO_SetBits(GPIOC,GPIO_Pin_13);		//P13引脚输出高电平
    		GPIO_ResetBits(GPIOC, GPIO_Pin_13); 	//P13引脚输出低电平	
    	}   
    }
    
    

    2.2 :方式2点灯

    main.c

    #include "stm32f10x.h"
    #include "led.h"
    
    int  main()
    {
    	LED_Init(); //LED初始化
    	
    	while(1)
    	{
    		//方式2
    		//GPIO_WriteBit(GPIOC,GPIO_Pin_13, Bit_SET);   //P13引脚输出高电平
    		GPIO_WriteBit(GPIOC,GPIO_Pin_13, Bit_RESET); //P13引脚输出低电平
    	}   
    }
    
    

    2.3 :方式3点灯

    main.c

    #include "stm32f10x.h"
    #include "led.h"
    
    int  main()
    {
    	LED_Init(); //LED初始化
    	
    	while(1)
    	{
    		//方式3
    		//GPIO_Write(GPIOC, 0X2000); 	 	//P13引脚输出高电平
    		GPIO_Write(GPIOC, 0X0000); 		//整组端口输出低电平
    	}   
    }
    
    

    点灯效果

    在这里插入图片描述

    3:STM32闪烁灯

    加一个延时函数 我们就可以做一个 LED闪烁效果了
    这里的延时我们没有用到定时器 ,只是简单的通过循环来延时。

    delay.c

    #include "stm32f10x.h"
    #include "delay.h"
    
    void delay_ms(uint16_t time)
    {
    	uint16_t i = 0;
    	while(time--)
    	{
    		i = 10000;
    		while(i--);
    	}
    }
    
    
    

    main.c

    #include "stm32f10x.h"
    #include "led.h"
    #include "delay.h"
    
    int  main()
    {
    	LED_Init(); //LED初始化
    	
    	while(1)
    	{
    		GPIO_ResetBits(GPIOC, GPIO_Pin_13); 	//LED点亮
    		delay_ms(1000); //亮灯大约1秒
    		GPIO_SetBits(GPIOC,GPIO_Pin_13);		//LED熄灭
    		delay_ms(1000); //熄灭灯大约1秒
    	}   
    }
    
    

    闪烁灯效果

    在这里插入图片描述

    4:STM32呼吸灯

    呼吸灯就像我们人呼吸那样,我们可以让灯慢慢的亮 慢慢的暗,这个就是通过占空比来调节。(可以理解为LED高电平时间和LED低电平时间)
    比如 LED 亮延时100us 然后 暗 100us 这时候他们占空比是百分之50.
    那如果我们 LED 亮10us 然后暗 还是100us 这个时候 你会发现 LED灯变暗了很多。 这个其实是我们的视觉暂停 原因导致的。
    所以我们可以通过调节 LED点亮的时间 和 LED 熄灭的时间的比例来调节,让LED 慢慢的亮 然后 慢慢的 暗。

    慢慢的亮 意思就是 LED的低电平时间慢慢的增加。
    比如从1us 慢慢 到500us 然后LED高电平时间则是 从500us 慢慢到1us。
    如果是想着LED灯 亮慢慢 暗 则相反。

    main.c

    #include "stm32f10x.h"
    #include "led.h"
    #include "delay.h"
    
    int  main()
    {
    	LED_Init(); //LED初始化
    	unsigned char i = 0;
    	unsigned int t = 1;
    	unsigned char flag = 1;
    	while(1)
    	{
    		if(flag == 1) //LED由暗到亮
    		{
    			for(i=0;i<10;i++)
    			{
    				GPIO_ResetBits(GPIOC, GPIO_Pin_13); 	//LED点亮
    				delay_us(t);
    				GPIO_SetBits(GPIOC,GPIO_Pin_13);		//LED熄灭
    				delay_us(501-t);
    			}
    			t++;	
    			if(t == 500)
    			{
    				flag = 0;
    			}
    		}
    		if(flag == 0) //LED由亮到暗
    		{
    			for(i=0;i<10;i++)
    			{	
    				GPIO_ResetBits(GPIOC, GPIO_Pin_13); 	//LED点亮
    				delay_us(t);
    				GPIO_SetBits(GPIOC,GPIO_Pin_13);		//LED熄灭
    				delay_us(501-t);
    			}
    			t--;
    			if(t == 1)
    			{
    				flag = 1;
    			}
    		}
    	} 
    }
    
    

    呼吸灯效果

    在这里插入图片描述

    5:STM32按键点灯

    我们玩了呼吸灯 也可以玩玩按键点灯,按键其实也是挺简单的,我用的按键是只有两个引脚的,我们任意的一脚接单片机的GND ,另一个接单片机的GPIO 当按下按键 这个GPIO则会是低电平。 那我们就检测这个GPIO是否是低电平 如果是 我们则点亮LED灯。

    key.c

    #include "stm32f10x.h"
    #include "key.h"
    
    /*按键初始化函数*/
    void KEY_Init(void)
    {
    	//1.打开控制GPIOA的时钟(APB2)
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    	
    	//2.配置结构体	
    	GPIO_InitTypeDef key_init;
    	key_init.GPIO_Pin   = GPIO_Pin_5;      	//GPIOC13引脚
    	key_init.GPIO_Mode  = GPIO_Mode_IPU; 	//上拉输入	
    	//led_init.GPIO_Speed = GPIO_Speed_10MHz; //10MHz
    	
    	//3.对成员进行初始化
    	GPIO_Init(GPIOA, &key_init);
    }
    
    

    main.c

    #include "stm32f10x.h"
    #include "led.h"
    #include "key.h"
    #include "delay.h"
    
    int  main()
    {
    	LED_Init(); //LED初始化
    	KEY_Init(); //按键初始化
    	GPIO_SetBits(GPIOC,GPIO_Pin_13);	//LED设置高电平
    	GPIO_SetBits(GPIOA,GPIO_Pin_5);		//按键设置高电平
    	
    	while(1)
    	{
    		//如果按键被按下
    		if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5) == 0) 	
    		{
    				delay_ms(20); //消抖再次判断是否按下
    				if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5) == 0)
    				{
    					GPIO_ResetBits(GPIOC, GPIO_Pin_13); 	//LED点亮
    					delay_ms(1000); //延时大约1秒				
    				}
    		}
    		else
    		{
    				GPIO_SetBits(GPIOC,GPIO_Pin_13);		//LED熄灭
    		}
    	} 
    }
    
    

    按键点灯效果

    在这里插入图片描述

    6:STM32震动感应灯

    需要材料:

    6.1:震动传感器

    在这里插入图片描述

    2.继电器

    (我用的这个是高电平导通)
    在这里插入图片描述

    这种是震动传感器,我们可以也通过震动来点灯,我用的这个模块是当发生震动时,会变成低电平。那其实就是相当于我们的按键,我们去检测一下这个模块到底是不是低电平如果是低电平,那么我们让LED灯点亮1秒啥的都有可以。
    直接上代码:
    配置震动传感器模块代码

    代码

    shake.c

    #include "stm32f10x.h"
    #include "shake.h"
    
    /*震动传感器初始化函数*/
    void SHAKE_Init(void)
    {
    	//打开GPIOC时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    	
    	//配置结构体
    	GPIO_InitTypeDef shake_init;
    	shake_init.GPIO_Mode  = GPIO_Mode_IPU; 	//上拉输入
    	shake_init.GPIO_Pin   = GPIO_Pin_3;		//A3引脚
    	//shake_init.GPIO_Speed = GPIO_Speed_50MHz;
    	
    	//成员初始化
    	GPIO_Init(GPIOA, &shake_init);		
    }
    
    

    继电器模块配置代码:

    relay.c

    #include "relay.h"
    
    /*继电器模块初始化函数*/
    void RELAY_Init(void)
    {
    	//打开时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    	//配置结构体
    	GPIO_InitTypeDef relay_init;
    	relay_init.GPIO_Mode  = GPIO_Mode_Out_PP; 	//推挽输出
    	relay_init.GPIO_Pin   = GPIO_Pin_4;			//3引脚
    	relay_init.GPIO_Speed = GPIO_Speed_50MHz;
    	
    	//成员初始化
    	GPIO_Init(GPIOA, &relay_init);
    }
    
    

    main.c

    #include "stm32f10x.h"
    #include "led.h"
    #include "relay.h"
    #include "delay.h"
    #include "shake.h"
    
    /*主程序入口*/
    int  main()
    {
    	LED_Init();  //LED模块初始化
    	RELAY_Init();//继电器模块初始化
    	SHAKE_Init();//震动传感器初始化
    	
    	GPIO_SetBits(GPIOC, GPIO_Pin_13); 	//LED高电平
       	GPIO_SetBits(GPIOA, GPIO_Pin_3); 	//震动传感器高电平
    	GPIO_ResetBits(GPIOA, GPIO_Pin_4); 	//继电器低电平(关闭)
    	while(1)
    	{
    		if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3) == 0)
    		{
    			GPIO_ResetBits(GPIOC, GPIO_Pin_13); //LED点亮
    			GPIO_SetBits(GPIOA, GPIO_Pin_4); 	//继电器导通 (高电平)
    			delay_ms(1000);
    		}
    		else
    		{
    			GPIO_SetBits(GPIOC, GPIO_Pin_13); 	//LED不亮
    			GPIO_ResetBits(GPIOA, GPIO_Pin_4); 	//继电器不导通 (低电平)
    		}
    			
    	} 
    }
    
    

    如何接线

    STM32震动传感器
    3.3VVCC
    GNDGND
    A3DO
    STM32继电器
    5VVCC
    GNDGND
    A4IN
    GND公共端
    STM32LED灯
    3.3V正极
    继电器LED灯
    常开端负极

    项目效果

    在这里插入图片描述

    如果觉得这篇文章对你有用。欢迎大家点赞、评论哈哈

    需要整个工程代码和 模块的相关资料,私信我 或者 +我扣扣:844797079 。或者评论区留下的你的联系方式。

    继续加油!

    展开全文
  • 一、硬件连接:

    一、硬件连接:

    展开全文
  • LED灯连接原理图如图所示,控制对应IO口输出低电平则LED灯点亮。其中LED灯连接IO口为GPB5、6、7、8。 程序如下: #define GPBCON (*(volatile unsigned int *)0x56000010) //GPB三个IO寄存器宏定义 #define ...
    LED灯连接原理图如图所示,控制对应IO口输出低电平则LED灯点亮。其中LED灯连接IO口为GPB5、6、7、8。
    


    程序如下:
    #define GPBCON (*(volatile unsigned int *)0x56000010) //GPB三个IO寄存器宏定义
    #define GPBDAT (*(volatile unsigned int *)0x56000014)
    #define GPBDUP (*(volatile unsigned int *)0x56000018)

    #define CLBIT(n) ((1<<n)&0xf)

    int main()
    {
            char n,i;
            int j;
           //由图知道GPB5、6、7、8需要输出
            GPBCON &= (~(3<<10)|(3<<12)|(3<<14)|(3<<16));
            GPBCON |=  ((1<<10)|(1<<12)|(1<<14)|(1<<16));
            //禁止上拉电阻
            GPBDUP |= ((1<<5)|(1<<6)|(1<<7)|(1<<8));

      while(1)
            {
                    for(i=0;i<4;i++)
                    {
                            n = CLBIT(i);
                           //将数据口5、6、7、8位置1,其他位不变
                            GPBDAT |= ((1<<5)|(1<<6)|(1<<7)|(1<<8));
                           //循环使将数据口5、6、7、8位某一位置0另3位置1。
                            GPBDAT &= (~(n<<5));
                           //延时一段时间
                            for(j=0;j<300000;j++);
                    }
            }               
           
    }
    展开全文
  • 上篇博文以globalmem为例实现了一个虚拟的字符设备驱动,本文将在上文的基础上,以点亮LED实例来介绍GPIO字符设备驱动。 环境:主机-Ubuntu 16.04,开发板-友善之臂tiny4412开发板,内核版本linux-3.5,参考tiny...

    上篇博文以globalmem为例实现了一个虚拟的字符设备驱动,本文将在上文的基础上,以点亮LED实例来介绍GPIO字符设备驱动,将不重复上篇相同内容。

    环境:主机-Ubuntu 16.04,开发板-友善之臂tiny4412开发板,内核版本linux-3.5,参考tiny4412相关手册。

    板上硬件资源:

    注:实践发现,本开发板实际使用的分别是:GPM4_0、GPM4_1、GPM4_2、GPM4_3(可能硬件版本不一)

    重点意外:Tiny4412自带内核已把LED驱动编进了内核,因此,需重新配置内核将其取消或编译成模块,再重新烧写内核。

     

    一、GPIO相关寄存器简介

    Tiny4412采用的是Samsung ARM Cortex-A9 四核 Exynos 4412 Quad-core处理器,运行主频1.5G。。。

    CPU寄存器相关的关键是看用户手册(User's Manual)

    《Exynos 4412 SCP_Users Manual_Ver.0.10.00_Preliminary0.pdf》

    GPIO相关的章节见 --- “ 6 General Purpose Input/Ouput (GPIO) Control

    看到 6.2 Register Description ,6.2.1是总的概述,6.2.2~5是具体说明。

    GPIO相关寄存器主要有以下几种:

    寄存器描述备注
    GPxxCONconfiguration register配置寄存器,配置输入/输出/IO复用等功能
    GPxxDATdata register数据寄存器,读取输入数据/设置输出数据
    GPxxPUDpull-up/down register上/下拉寄存器,配置上下拉状态
    GPxxDRVdrive strength control register驱动强度控制寄存器,配置IO驱动能力
    GPxxCONPDNpower down mode configuration register掉电模式配置寄存器,配置输入/输出
    GPxxPUDPDNpower down mode pull-up/down register掉电模式上下拉寄存器,配置上下拉状态

    还有关键的,寄存器地址及其详细描述,如下:

    1、配置寄存器GPxxCON:

    其地址Address就是:Base Address(基地址)+offset(偏移),即 0x1100 0000 + 0x02E0;

    一个寄存器是4个字节32bit大小,按位定义每4bit一组共8组分别对应GPM4_0~7等8个IO。(上图不完整)

    再看右边的数值定义,0x0 = Input, 0x1=Output ...等已说明很具体了,只不过是给对应的位赋值这么简单。

    2、数据寄存器GPxxDAT:

    同理,[0~7]位分别对GPM4_0~7,还说:当配置成输入时,读取对应位的值就是对应IO的状态了;配置成输出时,写入对应位的值就是对应IO的状态了;当配置成功能引脚时如(UART),其值是未定义的。

    3、上下拉寄存器GPxxPUD:

    2n+1:2n即2位对应一个IO,根据Description的定义来赋值。

    。。。其他寄存器就不一一讲了,大同小异,看手册就可以了。

    操作某个IO(UART、I2C、SPI等所有片上外设),最终目的都是通过给这些寄存器赋值使其工作起来。

     

    二、寄存器操作

    直接操作寄存器是一种简单粗暴的方法:

    简单---只需知道寄存器地址、各bit的作用就可以了;

    粗暴---直接进行地址操作,直接读写地址的值。

    以32位CPU为例,读写操作如下:???

    /* 读32位寄存器的值 */
    unsigned int reg32_read(unsigned int addr)
    {
    	return *(volatile unsigned int *)addr;
    }
    
    /* 写32位寄存器的值 */
    void reg32_write(unsigned int addr, unsigned int data)
    {
    	*(volatile unsigned int *)addr = data;
    }
    

    当然,上述几行代码虽简单,在裸奔的单片机或实时系统等方案上可行,

    但是,linux内核不允许直接操作物理地址PA,只能操作虚拟地址VA,其提供一套机制来进行映射转换:

    /*    io内存映射,将物理地址映射为虚拟地址
     *    cookie --- physical addr 物理地址
     *    size --- 映射大小
     * 
     *    return --- virtual addr 映射后的虚拟地址
     */
    #define ioremap(cookie,size)		__arm_ioremap((cookie), (size), MT_DEVICE)
    
    
    /*    取消映射
     *    addr--- ioremap得到的地址
     * 
     */
    void iounmap(void *addr)
    
    
    /* 读出io内存映射地址c中的32位值 */
    #define readl(c)		({ u32 __v = readl_relaxed(c); __iormb(); __v; })
    
    /* 向io内存映射地址c中写入32位值v */
    #define writel(v,c)		({ __iowmb(); writel_relaxed(v,c); })
    
    

    因此,linux要操作寄存器需作IO内存映射处理,再在映射的内存地址上进行操作;

    说白了就是,驱动也不能直接访问物理地址PA,要先将PA映射成VA,再在VA上操作,也能达到访问PA寄存器的效果。

     

    GPIO如何操作?以点亮LED为例:

    0、ioremap()将IO进行内存映射,得到可操作的虚拟内存(地址);

    1、在映射地址中,设置控制寄存器(GPXX_CON):Output模式;

    2、在映射地址中,设置上下拉寄存器(GPXX_PUD):不上下拉;

    3、设置数据寄存器(GPXX_DAT):往对应位写0或1;

    注意:写值时,应先读出某个寄存器的值,再对相应位进行操作,其他位应保留原值。

    详情见以下代码!

     

    三、GPIO相关操作函数简介

    以GPIO函数的方式,则需用到以下函数:

    /* 申请IO资源 */
    int gpio_request(unsigned gpio, const char *label)
    
    /* 释放IO资源 */
    void gpio_free(unsigned gpio)
    
    /* 配置IO功能/模式---配置CON寄存器 */
    int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
    
    /* 配置IO上下拉模式---配置PUD寄存器 */
    int s3c_gpio_setpull(unsigned int pin, samsung_gpio_pull_t pull)
    
    /* 设置IO的输出值---配置DAT寄存器 */
    void gpio_set_value(unsigned int gpio, int value)

    可见,除申请IO资源外,其他GPIO函数的最终目的---也是设置相应的寄存器,流程与操作寄存器方式类似。

    具体应用见以下代码!

     

    四、自动创建设备类及设备节点

    如何在字符驱动中自动创建设备节点,而不需手动通过命令mknod来创建呢?

    由如下函数实现:

    /* 创建设备类 */
    /* This is a #define to keep the compiler from merging different
     * instances of the __key variable */
    #define class_create(owner, name)		\
    ({						\
    	static struct lock_class_key __key;	\
    	__class_create(owner, name, &__key);	\
    })
    
    /* 销毁设备类 */
    void class_destroy(struct class *cls)
    
    
    /* 创建设备并注册到sysfs */
    struct device *device_create(struct class *class, struct device *parent,
    			     dev_t devt, void *drvdata, const char *fmt, ...)
    
    /* 销毁设备 */
    void device_destroy(struct class *class, dev_t devt)
    
    
    

    内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
     

    五、源码

    首先,驱动模型还是那一套(上篇有介绍),驱动加载时创建设备类及设备(init),打开设备时申请资源及配置功能(open),控制设备时根据命令设备引脚电平(ioctl),。。。

    本例程实现了两种驱动方式 --- IO映射方式、GPIO函数方式,通过宏 IOMAP_REG_ACCESS 控制。

    1、GPIO驱动源码:

    gpio_led_drv.c:

    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/gpio.h>
    #include <plat/gpio-cfg.h>
    #include <linux/io.h>
    
    //#define IOMAP_REG_ACCESS		// 操作IO寄存器方式实现
    
    /* 控制命令 */
    enum {
    	LED_ALL_ON,
    	LED_ALL_OFF,
    };
    
    #define LED_VAL_ON		0
    #define LED_VAL_OFF		1
    
    static int led_gpios[] = {
    	EXYNOS4X12_GPM4(0),
    	EXYNOS4X12_GPM4(1),
    	EXYNOS4X12_GPM4(2),
    	EXYNOS4X12_GPM4(3),
    };
    
    #define LED_COUNT		ARRAY_SIZE(led_gpios)
    
    /* 实例化led */
    dev_t devon;
    struct cdev led_cdev;
    struct class *dev_class = NULL;
    struct device *dev_led = NULL;
    void *va_base_p2 = NULL;
    
    #define CON_MODE_OUTPUT		0x1				// output mode
    #define PA_BASE_ADDR_P2		0x11000000		// physical address
    #define OFFSET_GPM4_CON		0x02E0			// control reg
    #define OFFSET_GPM4_DAT		0x02E4			// data reg
    
    ssize_t gpio_led_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
    {
    	printk("%s -------- enter ...\n", __FUNCTION__);
    	return 0;
    }
    
    ssize_t gpio_led_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
    {
    	printk("%s -------- enter ...\n", __FUNCTION__);
    	return 0;
    }
    
    /* 控制命令处理函数 */
    long gpio_led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
    	int i;
    
    	switch(cmd)
    	{
    		case LED_ALL_ON:
    			for(i=0; i<LED_COUNT; i++)
    			{
    			#ifdef IOMAP_REG_ACCESS
    				writel(0x00, va_base_p2+OFFSET_GPM4_DAT);
    			#else
    				gpio_set_value(led_gpios[i], LED_VAL_ON);
    			#endif
    			}
    			break;
    
    		case LED_ALL_OFF:
    			for(i=0; i<LED_COUNT; i++)
    			{
    			#ifdef IOMAP_REG_ACCESS
    				writel(0xFF, va_base_p2+OFFSET_GPM4_DAT);
    			#else
    				gpio_set_value(led_gpios[i], LED_VAL_OFF);
    			#endif
    			}
    			break;
    
    		default:
    			break;
    	}
    
    	return 0;
    }
    
    int gpio_led_open(struct inode *inode, struct file *filp)
    {
    	char label_name[] = "led_M4_x";
    	int reg_val;
    	int i, ret;
    	
    	printk("%s -------- enter ...\n", __FUNCTION__);
    
    #ifdef IOMAP_REG_ACCESS
    	/* 映射寄存器IO地址空间 */
    	va_base_p2 = ioremap(PA_BASE_ADDR_P2, 0x02FF);
    
    	/* 先读出配置寄存器值-再修改(其他位保留原值) */
    	reg_val = readl(va_base_p2 +OFFSET_GPM4_CON);
    	for(i=0; i<LED_COUNT; i++)
    	{
    		/* 将reg对应4位设置为output模式 */
    		reg_val = (reg_val&(~(0xF<<(i*4)))) | (CON_MODE_OUTPUT<<(i*4));
    	}
    	writel(reg_val, va_base_p2 +OFFSET_GPM4_CON);
    #else
    	/* 申请GPIO资源 */
    	for(i=0; i<LED_COUNT; i++)
    	{
    		label_name[strlen(label_name)-1] = '0'+i;
    		ret = gpio_request(led_gpios[i], label_name);
    		if(ret < 0)
    			goto ERR_GPIO_REQ;
    		
    		s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
    		s3c_gpio_setpull(led_gpios[i], S3C_GPIO_PULL_NONE);
    		gpio_set_value(led_gpios[i], LED_VAL_OFF);
    	}
    #endif
    	return 0;
    	
    #ifndef IOMAP_REG_ACCESS
    	ERR_GPIO_REQ:
    	for(i-=1; i>=0; i--)
    	{
    		gpio_free(led_gpios[i]);
    	}
    
    	return -1;
    #endif
    }
    
    int gpio_led_release(struct inode *inode, struct file *filp)
    {
    	int i;
    
    	printk("%s -------- enter ...\n", __FUNCTION__);
    
    #ifdef IOMAP_REG_ACCESS
    	/* 取消内存映射 */
    	iounmap(va_base_p2);
    #else
    	/* 释放GPIO资源 */
    	for(i=0; i<LED_COUNT; i++)
    		gpio_free(led_gpios[i]);
    #endif
    	return 0;
    }
    
    /* 文件操作结构体 */
    static const struct file_operations gpio_led_fops = 
    {
    	.owner = THIS_MODULE,
    	.read = gpio_led_read,
    	.write = gpio_led_write,
    	.unlocked_ioctl = gpio_led_ioctl,
    	.open = gpio_led_open,
    	.release = gpio_led_release,
    };
    
    /* 加载函数 */
    static int __init gpio_led_init(void)
    {
    	int ret;
    
    	ret = alloc_chrdev_region(&devon, 0, LED_COUNT, "gpio_led");
    	if(ret < 0)
    		return -1;
    
    	/* 初始化cdev, 并将设备注册到内核 */
    	cdev_init(&led_cdev, &gpio_led_fops);
    	ret = cdev_add(&led_cdev, devon, LED_COUNT);
    	if(ret < 0)
    		goto ERR_CDEV_ADD;
    
    	/* 创建设备类(/sys/class下可查看) */
    	dev_class = class_create(THIS_MODULE, "led_class");
    	if(IS_ERR(dev_class))
    	{
    		goto ERR_CLASS_CREATE;
    	}
    
    	/* 创建设备文件(/dev下可查看) */
    	dev_led = device_create(dev_class, NULL, devon, NULL, "led");
    	if(IS_ERR(dev_led))
    		goto ERR_DEV_CREATE;
    
    	printk("%s successfully\n", __FUNCTION__);
    	return 0;
    
    	ERR_DEV_CREATE:
    	class_destroy(dev_class);
    	
    	ERR_CLASS_CREATE:
    	cdev_del(&led_cdev);
    	
    	ERR_CDEV_ADD:
    	unregister_chrdev_region(devon, LED_COUNT);
    
    	return -1;
    }
    
    /* 卸载函数 */
    static void __exit gpio_led_exit(void)
    {
    	
    	/* 注销设备 */
    	device_destroy(dev_class, devon);
    
    	/* 注销设备类 */
    	class_destroy(dev_class);
    
    	/* 注销cdev */
    	cdev_del(&led_cdev);
    
    	/* 注销设备号 */
    	unregister_chrdev_region(devon, LED_COUNT);
    
    	printk("%s~\n", __FUNCTION__);
    }
    
    module_init(gpio_led_init);
    module_exit(gpio_led_exit);
    
    MODULE_AUTHOR("zengzr");
    MODULE_LICENSE("GPL v2");
    
    
    

     

    2、测试应用源码:

    如何测试?流程如下:先打开设备,再对4个LED进行开1秒关1秒,循环5次,最后关闭设备,退出。

    可在板上观察LED闪烁状态。

    led_test.c:

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    /* 设备文件名 */
    #define LED_DEV_NAME    "/dev/led"
    
    /* 控制命令 */
    enum {
        LED_ALL_ON,
        LED_ALL_OFF,
    };
    
    /* LED测试程序: 开关5次 */
    int main(void)
    {
        int fd = 0;
        int flag = 0;
    
        fd = open(LED_DEV_NAME, O_RDWR);
        if(fd < 0)
        {
            printf("%d: open failed!\n", fd);
            return -1;
        }
    
        while(flag < 5)
        {
            ioctl(fd, LED_ALL_ON, 0);
            sleep(1);
            ioctl(fd, LED_ALL_OFF, 0);
            sleep(1);
            flag++;
        }
    
        close(fd);
    
        return 0;
    }

     

    3、Makefile

    # make to build modules
    
    obj-m := gpio_led_drv.o
    
    KERNELDIR ?= /data/arm-linux/kernel/tiny4412/linux-3.5
    PWD := $(shell pwd)
    
    all: modules
    
    modules:
    	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    
    clean:
    	rm -rf *.o *.ko *mod* *.sy* *ord* .*cmd .tmp*
    

     

    完!

    展开全文
  • 其实题目所说的并不是绝对的,这个是需要看外围电路的,不一定是输入低电平LED就会亮,首先我们要知道亮是因为两侧有电压差,当LED的正极接到VCC,负极接到单片机的P1.0上,当单片机的P1.0为高电平时,此时LED两端...
  • main.c #include "s5pc100.h" void delay(int ms) { int i; while (ms--) { i = 4000; while (i--); } } int main() { int num = 0;...//将GPG3端口与LED相连的引脚设置为输出模式 GP
  • vhdl中点亮led灯程序

    2012-09-24 14:52:37
    vhdl中点亮led灯的基础程序,简洁明了
  • 基于pic4011的循环点亮LED灯程序
  • 点亮LED程序

    2014-12-19 18:00:26
    点亮LED程序
  • 单片机点亮led程序

    2014-05-26 20:12:12
    单片机点亮LED灯程序,只有一个LED灯,利用定时器精确定时1秒,适合刚接触定时器的初学者!
  • 点亮一个LED灯程序

    2015-02-12 19:17:25
    最简单的点亮1个LED灯程序1,通过该程序,了解如何控制单片机端口电平。
  • 继第一节第一个驱动程序框架记录之后,本篇文章将会在上一篇驱动程序的框架下编写点亮LED的驱动程序,同样会对上一个框架进行修改,优化。接下来进入正题 1、点亮LED程序框架分析 在最开始之前先来梳理一下点亮LED...
  • "学习51单片机的时候,第一个程序点亮LED灯现在开始学习到STM32第一个程序必须也得是点亮LED灯因为“祖师爷”说过当能够输出"Hello World!"或者点亮LED灯的时候,就掌握了90%的知识,可以出师了1注:虽然现在趋势...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,073
精华内容 4,829
关键字:

点亮led灯的程序