不同平台下的点灯代码,你在点灯的哪个段位?

简介: 不同平台下的点灯代码,你在点灯的哪个段位?

[序言]
   学习嵌入式开发的基本都是先从C语言开始学起的,而你的C语言的第一份代码也离不开一个“hello world!”的输出,自此走向了一条“不归路”。而这也仅仅是你学习C语言的过程,当你再深入的学习后,掌握C语言的基本语法,你开始慢慢的打开硬件的大门,接触了各种各样的硬件模块,LED、蜂鸣器、按键、LCD、陀螺仪、GPS、传感器、以太网、USB等等,而大家也基本都是不约而同的从点亮一个LED开始,了解硬件的控制原理以及嵌入式代码的开发流程,慢慢熟悉代码的编写、编译、烧录、运行的过程。

   自此,当你学习完一个平台后继续学习下一个平台的开发时,你的第一个项目不出意外的应该也是点亮一个LED灯,那么在不同的开发平台及操作系统下,点灯的代码有什么区别呢?今天就来一起探讨下在51、Arduino、STM32、Linux下的点灯代码吧,各位读者也可以看下你们都在哪个段位呢?

   “点灯大师”也是一直被网友调侃,各种花式点灯、流水灯、RGB灯、呼吸灯、频谱灯等等,从入门到点灯,再到放弃。

[硬件电路]
   先来看下我们今天的点灯代码基于的硬件电路吧。

   从图上不难知道,LED_GPIO给低电平的时候点亮LED,高电平则关闭LED。下面就来看下各个平台下是怎么控制LED的吧!

[51]


P1 = 0xFE;  /* 点亮LED */

   完事了,把LED接在P10口上就能点亮这个LED。代码最简单。

[Arduino]

void setup() 
{
  pinMode(3, OUTPUT);    /* 设置GPIO3为输出模式 */
}
void loop()
{
  digitalWrite(3, LOW);  /* 点亮LED */
  digitalWrite(3, HIGH); /* 关闭LED */
}

  代码也很简单,比51多了一个配置GPIO的输入/输出方向。


[STM32]

#include "stm32f4xx.h"
int main(void) {
    // 初始化系统时钟
    SystemInit();
    // 使能GPIOC时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    // 配置GPIOC引脚为推挽输出模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    while (1) 
    {
        // 关闭LED
        GPIO_SetBits(GPIOC, GPIO_Pin_13);
        // 点亮LED
        GPIO_ResetBits(GPIOC, GPIO_Pin_13);
    }
}

     到这里好像就有点感觉了,代码变复杂了,但是,还是能看的,也就多了时钟、模式、频率、上下拉的配置项,全部配置完之后就可以用标准库提供的接口来控制LED了。


[Linux]
    Linux下点亮一个LED就复杂很多了,先简单介绍下思路。

       1- 找到设备树,添加设备节点,在设备节点中描述GPIO引脚信息。

       2- 根据linux的字符设备的驱动框架写驱动程序

       3- 编写Makefile,把写好的驱动编译成一个模块

       4- 在开发板上加载模块

   以上可以通过加载和卸载驱动的时候点亮LED,但终归是在驱动上做的控制LED,为了完善,你还要继续编写代码,写一个应用程序去控制LED,最终实现在命令行去控制LED或自动的运行。所以还需要有下面的操作。

       5- 编写led app程序,并使用交叉编译器编译

       6- 在开发板上运行app程序,点亮led


   怎么样,是不是看起来都复杂很多,下面来看下代码实现,分为驱动程序、Makefile、APP程序、设备树。


驱动代码:

led.c:

#include "led.h"
struct chardev char_dev;
/* 函数申明 */
static int char_open(struct inode *inode, struct file *pfile);
static int char_release(struct inode *inode, struct file *pfile);
static ssize_t char_read(struct file *pfile, char __user *buf, size_t len, loff_t *offt);
static ssize_t char_write(struct file *pfile, const char __user *buf, size_t len, loff_t *offt);
static struct file_operations char_dev_fops = {
    .owner   = THIS_MODULE,
    .open    = char_open,
    .release = char_release,
    .read    = char_read,
    .write   = char_write,
};
/*
* @ brief : Open the character device driver.
* @ param : {struct inode *} inode: device node.
            {struct file * } pfile: device file, the file structure has a member variable called private_data Normally private_data is pointed to the device structure at open time.
* @ return: {int} ret: status.
* @ author: Barry
* @ modify: None
*/
static int char_open(struct inode *inode, struct file *pfile)
{
    /* 设置私有数据 */
    pfile->private_data = &char_dev;
    return 0;
}
/*
* @ brief : Open the character device driver.
* @ param : {struct inode *} inode: device node.
            {struct file * } pfile: device file, the file structure has a member variable called private_data Normally private_data is pointed to the device structure at open time.
* @ return: {int} ret: status.
* @ author: Barry
* @ modify: None
*/
static int char_release(struct inode *inode, struct file *pfile)
{
    return 0;
}
/*
* @ brief : Open the character device driver.
* @ param : {struct file *} pfile: device file.
            {char __user *} buf  : Data buffer returned to userspace.
            {size_t       } len  : Length of buf.
            {loff_t      *} offt : Offset relative to the first address of the file.
* @ return: {int} ret: status.
* @ author: Barry
* @ modify: None
*/
static ssize_t char_read(struct file *pfile, char __user *buf, size_t len, loff_t *offt)
{
    return 0;
}
/*
* @ brief : Open the character device driver.
* @ param : {struct file *} pfile: device file.
            {char __user *} buf  : Data buffer returned to userspace.
            {size_t       } len  : Length of buf.
            {loff_t      *} offt : Offset relative to the first address of the file.
* @ return: {int} ret: status.
* @ author: Barry
* @ modify: None
*/
static ssize_t char_write(struct file *pfile, const char __user *buf, size_t len, loff_t *offt)
{
    int ret = 0;
    unsigned char data = 0;
    struct chardev *dev = pfile->private_data;
    ret = copy_from_user(&data, buf, len);
    if(ret < 0)
    {
        printk("kernel write failed.\r\n");
        return -EFAULT;
    }
    printk("data:%d.\r\n", data);
    gpio_set_value(dev->gpio_num, (int)data);
    return 0;
}
/*
* @ brief : Character device driver entry functions.
* @ param : None
* @ return: {int} ret: status.
* @ author: Barry
* @ modify: None
*/
static int __init char_dev_init(void)
{
    int ret = 0;
    /* 获取设备节点:led-gpio1 */
    char_dev.node = of_find_node_by_path("/led-gpio1");
    if(char_dev.node == NULL)
    {
        printk("led-gpio1 node not find.\r\n");
        goto fail_find_node;
    }
    else
    {
        printk("find the led-gpio1 node.\r\n");
    }
    /* 获取设备树中的gpio属性,得到LED所使用的LED编号 */
    char_dev.gpio_num = of_get_named_gpio(char_dev.node, "gpios", 0);
    if(char_dev.gpio_num < 0)
    {
        printk("can't get led-gpio number.\r\n");
        goto fail_gpio_number;
    }
    printk("led gpio number:%d.\r\n", char_dev.gpio_num);
    ret = gpio_direction_output(char_dev.gpio_num, 1);
    if(ret < 0)
        printk("can't set gpio number:%d output high.\r\n", char_dev.gpio_num);
    gpio_set_value(char_dev.gpio_num, 1);
    /* 判断是否定义了设备号,定义了则走这个分支 */
    if(char_dev.major)
    {
        char_dev.devid = MKDEV(char_dev.major, 0);
        /* 注册设备号 */
        ret = register_chrdev_region(char_dev.devid, CHAR_DEV_NUM, CHAR_DEV_NAME);
        /* 注册失败 */
        if(ret != 0)
        {
            printk("Failed to register device number.\r\n");
            goto fail_devid;
        }
        else
        {
            printk("Register Device Number Successfully.\r\n");
        }
    }
    /* 没有定义设备号则先申请设备号 */
    else
    {
        /* 申请设备号 */
        ret = alloc_chrdev_region(&char_dev.devid, 0, CHAR_DEV_NUM, CHAR_DEV_NAME);
        /* 申请失败 */
        if(ret != 0)
        {
            printk("Failed to request device number. ret:%d\r\n", ret);
            goto fail_devid;
        }
        else
        {
            /* 获取分配的主设备号 */
            char_dev.major = MAJOR(char_dev.devid);
            /* 获取次设备号 */
            char_dev.minor = MINOR(char_dev.devid);
            printk("Successful application for device number. major number:%d, minor number:%d.\r\n", char_dev.major, char_dev.minor);
        }
    }
    /* 初始化cdev */
    char_dev.cdev.owner = THIS_MODULE;
    cdev_init(&char_dev.cdev, &char_dev_fops);
    /* 添加字符设备 */
    ret = cdev_add(&char_dev.cdev, char_dev.devid, CHAR_DEV_NUM);
    if(ret != 0)
        goto fail_cdev_add;
    /* 创建类 */
    char_dev.class = class_create(THIS_MODULE, CHAR_DEV_NAME);
    if(IS_ERR(char_dev.class))
    {
        ret = PTR_ERR(char_dev.class);
        goto fail_class_creat;
    }
    /* 创建设备 */
    char_dev.device = device_create(char_dev.class, NULL, char_dev.devid, NULL, CHAR_DEV_NAME);
    if( IS_ERR(char_dev.device))
    {
        ret = PTR_ERR(char_dev.device);
        goto fail_device_creat;
    }
    printk("%s Driver loaded successfully.\r\n", CHAR_DEV_NAME);
    return ret;
fail_device_creat:
    /* 删除类 */
    class_destroy(char_dev.class);
    printk("Device creation fails, destroying created classes.\r\n");
fail_class_creat:
    /* 删除cdev */
    cdev_del(&char_dev.cdev);
    printk("Class creation fails, deleting added character devices.\r\n");
fail_cdev_add:
    /* 注销设备号 */
    unregister_chrdev_region(char_dev.devid, CHAR_DEV_NUM);
    printk("Failure to add a character device, cancel the registered device number.\r\n");
fail_devid:
fail_gpio_number:
fail_find_node:
    return ret;
}
/*
* @ brief : Deregister the character device driver, go here when uninstalling the driver.
* @ param : None
* @ return: None
* @ author: Barry
* @ modify: None
*/
static void __exit char_dev_exit(void)
{   
    gpio_set_value(char_dev.gpio_num, 0);
    /* 删除cdev */
    cdev_del(&char_dev.cdev);
    printk("Delete cdev.\r\n");
    /* 注销设备号 */
    unregister_chrdev_region(char_dev.devid, CHAR_DEV_NUM);
    printk("Write-off equipment number.\r\n");
    /* 删除设备 */
    device_destroy(char_dev.class, char_dev.devid);
    printk("Delete device.\r\n");
    /* 删除类 */
    class_destroy(char_dev.class);
    printk("Delete class.\r\n");
    printk("%s Driver uninstallation is complete.\r\n", CHAR_DEV_NAME);
}
module_init(char_dev_init);
module_exit(char_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bagy");
MODULE_DESCRIPTION("Character device driver template program");

led.h:

#ifndef  __CHAR_DEV_TEMP_H__
#define  __CHAR_DEV_TEMP_H__ 
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#define  CHAR_DEV_NUM          1
#define  CHAR_DEV_NAME         "led_gpio1"
struct chardev
{
    dev_t               devid;         /* 设备号 */
    struct cdev         cdev;          /* cdev */
    struct class        *class;        /* 类 */
    struct device       *device;       /* 设备 */
    int                 major;         /* 主设备号 */
    int                 minor;         /* 次设备号 */
    struct device_node  *node;         /* 设备节点 */
    int                 gpio_num;      /* GPIO编号 */
};
#endif   /* __CHAR_DEV_TEMP_H__ */

Makefile:

KERNELDIR := /home/bagy/linux-code/kernel
CURRENT_PATH := $(shell pwd)
obj-m := led.o
build: kernel_modules
kernel_modules:
  $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
  $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

APP程序:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#define  LED_ON   1
#define  LED_OFF  0
int main(int argc, char *argv[])
{
    int fd = -1, ret = 0;
    char *filename = NULL;
    unsigned char data = 0;
    if(argc != 3)
    {
        printf("param error.\r\n");
        return -1;
    }
    filename = argv[1];
    fd = open(filename, O_RDWR);
    if(fd < 0)
    {
        printf("file %s open failed.\r\n", filename);
        return -1;
    }
    data = atoi(argv[2]);
    ret = write(fd, &data, 1);
    if(ret < 0)
    {
        printf("led control failed.\r\n");
        close(fd);
        return -1;
    }
    ret = close(fd);
    if(ret < 0)
    {
        printf("file close failed.\r\n");
        return -1;    
    }
    return 0;
}

设备树:

  led-gpio1 {
    compatible = "gpio1_led";
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_gpio1_leds>;
    gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;
    status = "okay";
  };

  好家伙,完活了,是不是有点懵,一下子咋这么复杂了,其实只要了解了字符设备的驱动框架就会觉得这个也没啥复杂的,就是使用一些现成的API接口。

     好了,到此文章就结束啦,后续小编将会在RK3588S上做一些有趣的开发,再分享给到公众号上。大家也可以评论区评论下当前的段位哦!

   如果觉得本篇文章多少有点帮助的话,求赞、关注、评论、转发,创作不易!你们的支持是小编创作最大动力。

相关文章
|
前端开发 芯片
【芯片前端】保持代码手感——交通灯
【芯片前端】保持代码手感——交通灯
|
6月前
|
存储
【期末不挂科-单片机考前速过系列P3】(第三章:13题MOV&MOVX&MOVC&数码管速过)经典例题盘点(带图解析)
【期末不挂科-单片机考前速过系列P3】(第三章:13题MOV&MOVX&MOVC&数码管速过)经典例题盘点(带图解析)
|
6月前
|
数据安全/隐私保护
【51单片机】初学者必学的一个矩阵键盘基本项目——(矩阵键盘密码锁)(8)
【51单片机】初学者必学的一个矩阵键盘基本项目——(矩阵键盘密码锁)(8)
|
6月前
|
传感器 监控 测试技术
单片机开发|基于51单片机的全自动黑板檫粉尘清除装置设计与实现
单片机开发|基于51单片机的全自动黑板檫粉尘清除装置设计与实现
HMI-36-【节能模式】开搞
今天主要是准备工作,先把运动模式中的接口都搬运过来,这样在主仪表中都可以把节能模式都的调用都写进去了。
HMI-36-【节能模式】开搞
|
6月前
|
传感器 人工智能 Java
单片机开发|基于51单片机的全自动黑板檫粉尘清除装置设计
教师常年在课堂上上课,粉笔的灰尘会对学生的健康造成很大的危害,很多教师都会患上肺部疾病。教师在课堂教学中,如何有效地消除粉尘对教师的身体造成的不良影响,是一个亟待解决的问题。本文以51单片机为核心,实现并设计一种全自动黑板檫粉尘清除装置。由黑板擦上的压力传感器进行检测,由51单片机来控制吸尘器(吸尘风机),以完成清理。本系统的主要功能包括:压力检测、按键控制、灯光显示、电扇除尘等。本文在对国内外有关文献进行整理和归纳的基础上,首先介绍了全自动黑板檫粉尘清除装置的研制背景和研究意义,并结合国内外的研究情况,提出了本文的主要研究内容和系统的设计思路。接着,对系统的硬件电路进行了详细的设计,并编写了
|
传感器 监控 测试技术
单片机开发|基于51单片机的全自动黑板檫粉尘清除装置设计与实现
教师常年在课堂上上课,粉笔的灰尘会对学生的健康造成很大的危害,很多教师都会患上肺部疾病。教师在课堂教学中,如何有效地消除粉尘对教师的身体造成的不良影响,是一个亟待解决的问题。本文以51单片机为核心,实现并设计一种全自动黑板檫粉尘清除装置。由黑板擦上的压力传感器进行检测,由51单片机来控制吸尘器(吸尘风机),以完成清理。本系统的主要功能包括:压力检测、按键控制、灯光显示、电扇除尘等。本文在对国内外有关文献进行整理和归纳的基础上,首先介绍了全自动黑板檫粉尘清除装置的研制背景和研究意义,并结合国内外的研究情况,提出了本文的主要研究内容和系统的设计思路。接着,对系统的硬件电路进行了详细的设计,并编写了
187 0
|
算法 Linux Android开发
动手智能小车记(5)-坦克底盘硬件模块大杂烩
动手智能小车记(5)-坦克底盘硬件模块大杂烩
245 0
|
芯片
点灯大师--IMX.6ULL阿尔法开发板点灯的步骤和五种方式(下)
点灯大师--IMX.6ULL阿尔法开发板点灯的步骤和五种方式
141 0
点灯大师--IMX.6ULL阿尔法开发板点灯的步骤和五种方式(下)
|
存储 移动开发 IDE
2022年十月份电赛OpenMV巡线方案详细代码分析(1)
2022年十月份电赛OpenMV巡线方案详细代码分析(1)
407 0