您尚未登录。

楼主 # 2021-12-19 17:35:51

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

全志点灯LED

全志V3s V3x F1C200S等都适用

驱动点灯
led.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>

#define DEV_MAJOR	200		/* 主设备号 */
#define DEV_NAME "myled"    /* 设备名  */
#define PB_CFG0_REG 0x01C20800 + 1 * 0x24 //PB配置寄存器 A:0 B:1 C:2 ....
#define PB_DATA_REG 0x01C20800 + 1 * 0x34 //PB数据寄存器 A:0 B:1 C:2 ....
#define PIN_N 5 //第5个引脚
#define N (PIN_N % 8 * 4)   //引脚x : x % 8 * 4
volatile unsigned int *gpio_con = NULL;
volatile unsigned int *gpio_dat = NULL;
/*打开设备*/
static int led_open (struct inode *node, struct file *filp)
{
   if (gpio_con) {
    /*打开成功*/
    }
    else {
   		return -1;
    }
    return 0;
}

/*写设备*/
static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off)
{
    unsigned char val;        
    copy_from_user(&val, buf, 1);
 
    if (val)
    {
   		*gpio_dat |= (1 << PIN_N);//引脚5设置1
    }
    else
    {
    	*gpio_dat &= ~(1 << PIN_N);//引脚5设置0
    }
 
    return 1; 
}

/*关闭设备*/
static int led_release (struct inode *node, struct file *filp)
{
    return 0;
}

/*
 * 设备操作函数结构体
 */
static struct file_operations myled_oprs = {
    .owner = THIS_MODULE,
    .open  = led_open,
    .write = led_write,
    .release = led_release,
};

/* 设备初始化 */ 
static int myled_init(void)
{
    int ret;
    ret = register_chrdev(DEV_MAJOR, DEV_NAME, &myled_oprs);
	if (ret < 0) {
		printk(KERN_ERR "fail\n");
		return -1;
	}  
	printk(KERN_ERR "init \n");

    gpio_con = (volatile unsigned int *)ioremap(PB_CFG0_REG, 1);
    //选择1
    gpio_dat = (volatile unsigned int *)ioremap(PB_DATA_REG, 1);
    //选择2
    //gpio_dat = gpio_con + 4;  //数据寄存器 指针+4是移动了4*4=16个字节 原来是0x24 现在是0x34

    *gpio_con &= ~(7 << N);  //7=111 取反000 20:22设置000 默认是0x7=111 失能
    *gpio_con |=  (1 << N);  //设置输出 20:22设置001

    *gpio_dat &= ~(1 << PIN_N);  //第5个引脚初始化设置0
    return 0;
}


static void __exit myled_exit(void)
{
	/* 注销字符设备驱动 */ 
	unregister_chrdev(DEV_MAJOR, DEV_NAME);
	printk("exit!\r\n");
}
//注册模块加载函数
module_init(myled_init);
//卸载模块加载函数
module_exit(myled_exit);
//开源信息
MODULE_LICENSE("GPL");
//作者
MODULE_AUTHOR("Lv129");

Makefile

ifneq ($(KERNELRELEASE),)
#kbuild syntax. dependency relationshsip of files and target modules are listed here.
obj-m := led.o
else
PWD:= $(shell pwd)

KDIR := /home/lzq/desktop/129/linux-5.10
all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules
	rm -rf .*.cmd *.o *.mod.c  .tmp_versions *.mod *.symvers *.order
clean:
	rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions *.mod *.symvers *.order
endif

测试应用

#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
/* ledtest on
 *   * ledtest off
 *     */
int main(int argc, char **argv)
{
	int fd;
	int a;
	unsigned char val = 1;
	if (argc != 3)
	{
		printf("Usage :\n");
		printf("%s your_dev <on|off>\n", argv[0]);
		return 0;
	}
 	fd = open(argv[1], O_RDWR);
	if (fd < 0)
	{
		printf("can't open!\n");
	}
	if (strcmp(argv[2], "on") == 0)
	{
		val  = 1;
	}
	else
	{
		val = 0;
	}
	write(fd, &val, 1);
	/* 关闭设备 */
	a = close(fd);
	if(a < 0){
		printf("Can't\r\n");
		return -1;
	}
	return 0;
}

Makefile

EXEC	= led_test
OBJS    = led_test.o
 
CROSS	= arm-linux-gnueabihf-
CC	     = $(CROSS)gcc 
STRIP	= $(CROSS)strip
CFLAGS	= -Wall -g -O2
 
all:  clean $(EXEC)
 
$(EXEC):$(OBJS)
	$(CC) $(CFLAGS) -o $@ $(OBJS)
	$(STRIP) $@
 
clean:
	-rm -f $(EXEC) *.o

离线

楼主 #1 2021-12-19 17:43:40

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

Re: 全志点灯LED

两个重要的寄存器
QQ截图20211219172622.jpg

配置引脚
.jpg

写入数据
.jpg

离线

楼主 #2 2021-12-19 17:56:40

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

Re: 全志点灯LED

make 获取led.ko 和可执行的led_test 弄到板子上
先查看已经注册的设备号 发现里面没有200

root@lzq:/test# cat /proc/devices
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input
 14 sound
 29 fb
 81 video4linux
 89 i2c
 90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
212 DVB
226 drm
248 rpmb
249 ttyGS
250 bsg
251 watchdog
252 media
253 rtc
254 gpiochip

Block devices:
  8 sd
 65 sd
 66 sd
 67 sd
 68 sd
 69 sd
 70 sd
 71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
259 blkext
root@lzq:/test# 

加载内核驱动 再次查看 出现200 myled

root@lzq:/test# insmod led.ko 
root@lzq:/test# cat /proc/devices
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input
 14 sound
 29 fb
 81 video4linux
 89 i2c
 90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
200 myled
212 DVB
226 drm
248 rpmb
249 ttyGS
250 bsg
251 watchdog
252 media
253 rtc
254 gpiochip

创建设备

root@lzq:/test# mknod /dev/myled c 200 0
root@lzq:/test# ls /dev/myled 
/dev/myled
root@lzq:/test# 

测试

root@lzq:/test# ./led_test /dev/myled on
root@lzq:/test# ./led_test /dev/myled off
root@lzq:/test# 

创建脚本测试

vim run.sh
chmod +x run.sh
./run.sh

run.sh

#!/bin/bash
while(true)
do
./led_test /dev/myled on
sleep 0.3
./led_test /dev/myled off
sleep 0.3
done

效果(PB5是屏幕背光的引脚)
.gif
卸载驱动 删除设备

rmmod myled
rm /dev/myled

最近编辑记录 资本家大善人 (2021-12-19 18:07:05)

离线

楼主 #3 2021-12-19 18:03:14

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

Re: 全志点灯LED

应用点灯
应用层直接用C操作

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <string.h>

#define GPIO_REG_BASE   0x01C20800      //GPIO物理基地址 (小页4kb)
#define MAP_SIZE        0x400 //MMU页大小
#define GPIO_BASE_OFFSET (GPIO_REG_BASE & 0X00000FFF) //GPIO基地址偏移计算
#define GPIO_PAGE_OFFSET (GPIO_REG_BASE & 0XFFFFF000) //获得页偏移
/**********************修改的************************/
#define rPB_CFG0 0X24  //PB_CFG0寄存器地址偏移
#define rPB_DAT 0X34    //PB_DAT寄存器地址偏移
/***************************************************/
int led_on(unsigned char *MAP_BASE);
int led_off(unsigned char *MAP_BASE);
int main(int argc, char **argv)
{
    static int dev_fd;
    unsigned char *map_base;

    if(argc!=2 || (strcmp(argv[1],"on") && strcmp(argv[1],"off"))){
        printf("argv_error!please input 'on' or 'off'!\n");
        exit (0);
    }

    dev_fd = open("/dev/mem", O_RDWR );
    if (dev_fd < 0){
        printf("open(/dev/mem) failed.\n");
        return 0;
    }
    map_base = (unsigned char *)mmap(NULL, 0x400,PROT_READ | PROT_WRITE, MAP_SHARED,dev_fd, GPIO_PAGE_OFFSET); //把物理地址映射到虚拟地址
    if(!strcmp(argv[1],"on")) led_on(map_base); //点亮LED
    if(!strcmp(argv[1],"off")) led_off(map_base);//关闭LED
    if(dev_fd) close(dev_fd);
    munmap(map_base,MAP_SIZE);//解除映射关系
    return 0;
}


//led_on
int  led_on(unsigned char *MAP_BASE)
{
    unsigned int PB_CFG0,PB_DAT;
    PB_CFG0=*(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_CFG0);
    PB_DAT=*(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_DAT);
    *(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_CFG0)=((PB_CFG0 & 0XFF0FFFFF)|0X00100000);//PB5 第6个引脚
    *(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_DAT)=((PB_DAT & 0XFFFFFFDF)|0X00000020);
}
//led_off
int  led_off(unsigned char *MAP_BASE)
{
    unsigned int PB_CFG0,PB_DAT;
    PB_CFG0=*(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_CFG0);
    PB_DAT=*(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_DAT);
    *(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_CFG0)=((PB_CFG0 & 0XFF0FFFFF)|0X00100000);//PB5 第6个引脚
    *(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_DAT)=((PB_DAT & 0XFFFFFFDF));
}

编译运行

arm-linux-gnueabihf-gcc led.c -o led
./led on
./led off

离线

楼主 #4 2021-12-19 18:23:44

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

Re: 全志点灯LED

另一种驱动代码(用到了GPIO的函数),会自动在/dev下创建设备

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <mach/gpio.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/device.h> // 内核源码里创建/移除设备,产生热插拔事件的函数class
 
 
#define MYMA  200
#define MYMI  3333
#define COUNT    1
 
#define LED_IO   GPIOB(5)//注意修改! 对应开发板上的led灯引脚
 
dev_t devid; //用于存放设备号
struct cdev mycdev; 
static struct class *mycls;
 
 
 
//打开设备文件时开启中断
static int myopen(struct inode *ind,struct file *fl)
{
    int ret;
    ret = gpio_request(LED_IO, "led_0"); //请求gpio口,tldev则是为其取一个名字。
    if(ret<0)
        goto err0;
    gpio_direction_output(LED_IO, 0); //配置gpio口为输出,先输出低电平
    return 0;
 
err0:
    return ret;
}
 
static ssize_t mywrite(struct file *fl,const char *__user buf,size_t len,loff_t *off)
{
    char stat;
 
    copy_from_user(&stat,buf,sizeof(char));
    if(stat)
        gpio_set_value(LED_IO,1);
    else
        gpio_set_value(LED_IO,0);
 
    return 0;
}
 
//关闭设备文件时释放中断
static int myclose(struct inode *inode,struct file *fl)
{
    gpio_set_value(LED_IO,0);
    gpio_free(LED_IO);
 
    return 0;
}
 
static struct file_operations fops= {
    .owner = THIS_MODULE,
    .open  = myopen,
    .write = mywrite,
    .release= myclose,
};
 
 
 
static int __init test_init(void)
{
    int ret;
 
    devid = MKDEV(MYMA, MYMI); //生成一个设备号
    ret = register_chrdev_region(devid, COUNT, DEVNAME); //name设备名(用于查看用, 长度不能超过64字节   )
    if (ret < 0)
        goto err0;
 
    cdev_init(&mycdev, &fops);
    mycdev.owner = THIS_MODULE;
    ret = cdev_add(&mycdev, devid, COUNT);
    if (ret < 0)
        goto err1;  
  
    mycls= class_create(THIS_MODULE,"mykeycls");
    if(mycls== NULL)
        goto err2;
    device_create(mycls,NULL,devid,NULL,DEVNAME);
 
 
    return 0;
 
err2:
    cdev_del(&mycdev);
err1:
    unregister_chrdev_region(devid, COUNT);
err0:
    return ret;
}
 
static void __exit test_exit(void)
{
    device_destroy(mycls,devid);
    class_destroy(mycls);
    unregister_chrdev_region(devid, COUNT);
    cdev_del(&mycdev);
    gpio_free(LED_IO); //释放gpio
}
 
module_init(test_init);
module_exit(test_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");
MODULE_DESCRIPTION("led");
MODULE_VERSION("v0.0");

Makefile 和一楼的一样
make报错出现无#include <mach/gpio.h>
解决:
1.将内核源码中的如图文件夹复制到arch/arm/include/
mach.jpg
这是linux3.4中的,我的是linux5.10,没有该文件夹
2.在内核源码arch/arm/include/下创建mach文件夹
创建gpio.h

/*
 * arch/arm/mach-sunxi/include/mach/gpio.h
 *
 * Copyright(c) 2013-2015 Allwinnertech Co., Ltd. 
 *         http://www.allwinnertech.com
 *
 * Author: sunny <sunny@allwinnertech.com>
 *
 * allwinner sunxi gpio defines.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */


#include <linux/slab.h>


#ifndef __SUNXI_MACH_GPIO_H
#define __SUNXI_MACH_GPIO_H


/* pin group base number name space,
 * the max pin number : 26*32=832.
 */
#define SUNXI_PINCTRL 	"sunxi-pinctrl"
#define SUNXI_BANK_SIZE 32
#define SUNXI_PA_BASE	0
#define SUNXI_PB_BASE	32
#define SUNXI_PC_BASE	64
#define SUNXI_PD_BASE	96
#define SUNXI_PE_BASE	128
#define SUNXI_PF_BASE	160
#define SUNXI_PG_BASE	192
#define SUNXI_PH_BASE	224
#define SUNXI_PI_BASE	256
#define SUNXI_PJ_BASE	288
#define SUNXI_PK_BASE	320
#define SUNXI_PL_BASE	352
#define SUNXI_PM_BASE	384
#define SUNXI_PN_BASE	416
#define SUNXI_PO_BASE	448
#define AXP_PIN_BASE	1024

#define SUNXI_PIN_NAME_MAX_LEN	8

/* sunxi gpio name space */
#define GPIOA(n)	(SUNXI_PA_BASE + (n))
#define GPIOB(n)	(SUNXI_PB_BASE + (n))
#define GPIOC(n)	(SUNXI_PC_BASE + (n))
#define GPIOD(n)	(SUNXI_PD_BASE + (n))
#define GPIOE(n)	(SUNXI_PE_BASE + (n))
#define GPIOF(n)	(SUNXI_PF_BASE + (n))
#define GPIOG(n)	(SUNXI_PG_BASE + (n))
#define GPIOH(n)	(SUNXI_PH_BASE + (n))
#define GPIOI(n)	(SUNXI_PI_BASE + (n))
#define GPIOJ(n)	(SUNXI_PJ_BASE + (n))
#define GPIOK(n)	(SUNXI_PK_BASE + (n))
#define GPIOL(n)	(SUNXI_PL_BASE + (n))
#define GPIOM(n)	(SUNXI_PM_BASE + (n))
#define GPION(n)	(SUNXI_PN_BASE + (n))
#define GPIOO(n)	(SUNXI_PO_BASE + (n))
#define GPIO_AXP(n)	(AXP_PIN_BASE  + (n))

/* sunxi specific input/output/eint functions */
#define SUNXI_PIN_INPUT_FUNC	(0)
#define SUNXI_PIN_OUTPUT_FUNC	(1)
#define SUNXI_PIN_EINT_FUNC     (6)
#define SUNXI_PIN_IO_DISABLE	(7)

/* axp group base number name space,
 * axp pinctrl number space coherent to sunxi-pinctrl.
 */
#define AXP_PINCTRL 	        "axp-pinctrl"
#define AXP_CFG_GRP 		(0xFFFF)
#define AXP_PIN_INPUT_FUNC	(0)
#define AXP_PIN_OUTPUT_FUNC	(1)
#define IS_AXP_PIN(pin)         (pin >= AXP_PIN_BASE)


/* sunxi specific pull up/down */
enum sunxi_pull_up_down {
	SUNXI_PULL_DISABLE = 0,
	SUNXI_PULL_UP,
	SUNXI_PULL_DOWN,
};

/* sunxi specific data types */
enum sunxi_data_type {
	SUNXI_DATA_LOW = 0,
	SUNXI_DATA_HIGH = 0,
};

/* sunxi specific pull status */
enum sunxi_pin_pull {
	SUNXI_PIN_PULL_DISABLE 	= 0x00,
	SUNXI_PIN_PULL_UP	= 0x01,
	SUNXI_PIN_PULL_DOWN	= 0x02,
	SUNXI_PIN_PULL_RESERVED	= 0x03,
};

/* sunxi specific driver levels */
enum sunxi_pin_drv_level {
	SUNXI_DRV_LEVEL0 = 10,
	SUNXI_DRV_LEVEL1 = 20,
	SUNXI_DRV_LEVEL2 = 30,
	SUNXI_DRV_LEVEL3 = 40,
};

/* sunxi specific data bit status */
enum sunxi_pin_data_status {
    SUNXI_PIN_DATA_LOW  = 0x00,
    SUNXI_PIN_DATA_HIGH = 0x01,
};

/* sunxi pin interrupt trigger mode */
enum sunxi_pin_int_trigger_mode {
    SUNXI_PIN_EINT_POSITIVE_EDGE   =   0x0,
    SUNXI_PIN_EINT_NEGATIVE_EDGE   =   0x1,
    SUNXI_PIN_EINT_HIGN_LEVEL      =   0x2,
    SUNXI_PIN_EINT_LOW_LEVEL       =   0x3,
    SUNXI_PIN_EINT_DOUBLE_EDGE     =   0x4
};

/* the source clock of pin int */
enum sunxi_pin_int_source_clk {
    SUNXI_PIN_INT_SRC_CLK_32K = 0x0,
    SUNXI_PIN_INT_SRC_CLK_24M = 0x1
};

static inline int sunxi_gpio_to_name(int gpio, char *name)
{
	int bank, index;
	if (!name) {
		return -EINVAL;
	}
	if (IS_AXP_PIN(gpio)) {
		/* axp gpio name like this : GPIO0/GPIO1/.. */
		index = gpio - AXP_PIN_BASE;
		sprintf(name, "GPIO%d", index);
	} else {
		/* sunxi gpio name like this : PA0/PA1/PB0 */
		bank = gpio / SUNXI_BANK_SIZE;
		index = gpio % SUNXI_BANK_SIZE;
		sprintf(name, "P%c%d", ('A' + bank), index);	
	}
	return 0;
}

#endif /* __SUNXI_MACH_GPIO_H */

测试代码

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
 
 
int main(int argc, char** argv)
{
    int fd;
    char LED_ON=1,LED_OFF=0,flag=0;
 
    fd = open("/dev/tldev", O_RDWR);
    if (fd < 0)
    {
        perror("open");
  
    }
   
    while (1)
    {
        write(fd,&LED_ON,1); //led灯亮
        sleep(1);
        write(fd,&LED_OFF,1); //led灯灭
        sleep(1);
 
    }
 
    return 0;
}   

运行后LED交替亮灭1s
本人已经验证成功

最近编辑记录 资本家大善人 (2021-12-19 18:24:23)

离线

#5 2021-12-19 18:37:36

regestday
会员
注册时间: 2021-08-15
已发帖子: 7
积分: 6

Re: 全志点灯LED

给楼主打CALL 学习了,是时候给板子加个外设接口了呀

离线

楼主 #6 2021-12-19 18:44:47

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

离线

楼主 #7 2021-12-19 19:13:53

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

Re: 全志点灯LED

应用层pwm点灯
PB5引脚有pwm功能,我们可以通过它来调节背光灯亮度

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

#define PIO_BASE_ADDR 0x01C20000
#define PIO_ADDR_OFF 0x800
#define PIO_CFG_OFF 0x24    //配置
#define PIO_DAT_OFF 0x34    //数据
#define PWM_ADDR_OFF 0x1400
#define PWM_CH1_OFF 0x08    //pwm1
#define Page_Size (4096 * 2)

uint32_t *base_map = NULL;
uint32_t *gpio_map = NULL;
uint32_t *gpio_cfg = NULL;
uint32_t *gpio_dat = NULL;
uint32_t *pwm_base_map = NULL;
uint32_t *pwm1_period = NULL;

uint32_t T = 1000;  //周期

int main(int argc , char **argv)
{
    int mem_fd;
    int duty=200;

    if (argc!=2)
    {
        printf("./backlight level (level:0~100)\n");
        exit(-1);
    }
    duty = T -  atol(argv[1])*10;
    if ((mem_fd = open("/dev/mem", O_RDWR)) < 0)
    {
        //printf("open error\r\n");
        exit(-1);
    }
    //mmap(系统自动分配内存地址,映射区长度“内存页的整数倍”,选择可读可写,MAP_SHARED=与其他所有映射到这个对象的进程共享空间,文件句柄,被映射内容的起点)
    //offest 映射物理内存的话,必须页对其!!!   所以这个起始地址应该是0x1000的整数倍,那么明显0x01C20800需要减去0x800才是整数倍!
    if ((base_map = (uint32_t *)mmap(NULL, Page_Size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, PIO_BASE_ADDR)) == NULL)
    {
        //printf("mmap error\r\n");
        close(mem_fd);
        exit(-1);
    }
    //printf("base_map:0x%.8X\n", (uint32_t)base_map);

    close(mem_fd);                                //映射好之后就可以关闭文件?
                                                  //这里已经将0x1c20000的地址映射到了内存中,但是我们需要的地址是0x01C20800,所以要再加上地址偏移量~
    gpio_map = (uint32_t)base_map + PIO_ADDR_OFF; //加上偏移量必选先把他转化成unsigend int才可以相加
    //printf("gpio_map:0x%.8X\n", (uint32_t)gpio_map);

    gpio_cfg = (uint32_t)gpio_map + PIO_CFG_OFF; //gpioB控制寄存器地址
    //printf("gpio_cfg:0x%.8X\n", (uint32_t)gpio_cfg);

    gpio_dat = (uint32_t)gpio_map + PIO_DAT_OFF; //gpioB数据寄存器地址
    //printf("gpio_dat:0x%.8X\n", (uint32_t)gpio_dat);

    /*PB5点灯*/
    (*gpio_cfg) &= ~((unsigned int)7 << 20); //先将对应位置0 //
    (*gpio_cfg) |= ((unsigned int)1 << 20);  //PB5 设定out //模式in out pwm

    // (*gpio_dat) &= ~(1 << 5); //开灯
    // sleep(1);
    // (*gpio_dat) |= (1<<5);
    // sleep(1);

    
/**
 * PWM波配置顺序
 * 1.GPIO 配置PWM输出模式
 * 2.PWM 预分頻 
 * 3.PWM 总周期
 * 4.PWM 活跃周期
 * 5.PWM 使能
 */
    //PB5设定PWM输出
    (*gpio_cfg) &= ~((unsigned int)7 << 20);    //置20:22 000
    (*gpio_cfg) |= ((unsigned int)2 << 20); //PB5 2=010 设定20:22 010 pwm1模式
    //我们需要的地址是0x01C21400,所以要再加上地址偏移量
    pwm_base_map = (uint32_t)base_map + PWM_ADDR_OFF; //pwm_base_map也是pwm控制寄存器 初始值是0x00000000
    //printf("pwm_base_map:0x%.8X\n", (uint32_t)pwm_base_map);

    /*首先设置pwm1 预分頻*/                  //PWM_CH1_PRESCAL
    (*pwm_base_map) &= ~((uint32_t)15 << 15); //先将15~18位置0
    (*pwm_base_map) |= (uint32_t)0 << 15;     //将15~18位设置为 0000 ---> 对应分頻120 24m/120=200k

    /*可能要设置SCLK_CH1_GATING为mask*/
    (*pwm_base_map) &= ~((uint32_t)1 << 21); //先将第21位置0 
    (*pwm_base_map) |= (uint32_t)1 << 21; //将第21位置1 ---> 设置为自定义预分頻系数
/**
 * 具体的总周期时间的作用需要进一步测试
 * 注意:要活动周期设置好之后使能PWM通道
 * 这样才会有正确输出,并且之后直接修改寄存器的值就可以修改占空比。
 * */
    /*再设置pwm1占空比*/
    pwm1_period = (uint32_t)pwm_base_map + PWM_CH1_OFF; //pwm1_period设置pwm_CH1的占空比寄存器
    //printf("pwm1_period:0x%.8X\n", (uint32_t)pwm1_period);

    /*先设置总周期*/ //PWM周期的计算应该是这样 OSC 24MHz / Pre-scalar / (entire cycles + 1)
    (*pwm1_period) &= ~((uint32_t)65535 << 16); //将31~16位置零    
    (*pwm1_period) |= (uint32_t)T << 16; // 现在设置整个周期65535

    /*再设置活跃周期  活跃周期要小于总周期*/
    (*pwm1_period) &= ~((uint32_t)65535 << 0); //将15~0位置零
    (*pwm1_period) |= (uint32_t)duty << 0; //将15~0位置为2500 现在设置活跃周期为35535

    /*最后应该设置PWM_CH1_EN为enable*/
    (*pwm_base_map) &= ~((uint32_t)1 << 19); //先将第4位置0 ---> disable
    (*pwm_base_map) |= (uint32_t)1 << 19;    //将第4位置1 ---> enable pwm1  
    //printf("PWM ENABLE DUTY:%d\n",duty);

    munmap(base_map, Page_Size);
    //printf("munmap success!\n");
    return 0;
}

0.配置
pwmmode.jpg
1.分频
.jpg
pass自定义
pass.jpg
2.周期
zk.jpg
3.使能
.jpg

测试

#!/bin/bash
t=0
while(true)
do
#sleep 0.001
.backlight $t
echo $((t++))
if [ $t == 101 ];then
	t=0
	echo "ok"
fi
done

效果
pwm效果.gif

最近编辑记录 资本家大善人 (2021-12-19 19:18:24)

离线

#8 2021-12-22 15:20:43

sunxiang
会员
注册时间: 2021-05-05
已发帖子: 222
积分: 121

Re: 全志点灯LED

这个如何编译到内核里面有没有例子呀,好期待哦

离线

楼主 #9 2021-12-22 15:37:17

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

Re: 全志点灯LED

sunxiang 说:

这个如何编译到内核里面有没有例子呀,好期待哦

参考其他资料改改就行,内核源码里创建led文件夹,放源码,改Makefile,menuconfig选上,最后编译就行

离线

#10 2021-12-23 08:14:40

yelong98
会员
注册时间: 2020-06-03
已发帖子: 97
积分: 59.5

Re: 全志点灯LED

不需要改设备树么

离线

#11 2021-12-23 09:15:15

mysteryli
会员
注册时间: 2020-03-05
已发帖子: 485
积分: 390
个人网站

Re: 全志点灯LED

用了楼主的方法尝试V3S pwm点灯,成功了,简单看了源码请问原理是不是直接在应用层操作底层寄存器?我想了解下linux的存储机制,既直接操作寄存器的原理,如何搜索关键字学习呢

离线

楼主 #12 2021-12-23 11:37:46

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

Re: 全志点灯LED

yelong98 说:

不需要改设备树么

这个帖子提供了好多种点灯的方法,没提到设备树的都不用改

离线

#13 2021-12-23 11:40:42

luciferseva
会员
注册时间: 2020-05-20
已发帖子: 58
积分: 51.5

Re: 全志点灯LED

该评论内容与本帖子无关,鼓励各位坑友积极发言讨论与帖子有关的内容!

离线

  • 不通过:其他

楼主 #14 2021-12-23 11:41:23

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

Re: 全志点灯LED

mysteryli 说:

用了楼主的方法尝试V3S pwm点灯,成功了,简单看了源码请问原理是不是直接在应用层操作底层寄存器?我想了解下linux的存储机制,既直接操作寄存器的原理,如何搜索关键字学习呢

/dev/mem设备+mmap+文件读写+数据手册

离线

楼主 #15 2021-12-23 14:32:13

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

Re: 全志点灯LED

led驱动编译到内核
在如图路径中放入led.c驱动代码 Kconfig Makefile
led0.jpg
led.c
查看一楼

Kconfig

menu "led driver"
config LED
tristate"myled"
default y
endmenu

Makefile

obj-$(CONFIG_LED)+=led.o

修改char路径下的Kconfig和Makefile
led1.jpg
在endmenu前加入

source "drivers/char/led/Kconfig"

led2.jpg

obj-$(CONFIG_LED)         	+= led/
make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

led3.jpg

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12

替换原来的zImage
启动系统查看加载的模块 200 myled

root@lzq:~# cat /proc/devices
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input
 29 fb
 81 video4linux
 89 i2c
 90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
200 myled
212 DVB
226 drm
248 rpmb
249 ttyGS
250 bsg
251 watchdog
252 media
253 rtc
254 gpiochip

Block devices:
  8 sd
 65 sd
 66 sd
 67 sd
 68 sd
 69 sd
 70 sd
 71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
259 blkext

最后手动创建myled的设备(代码里可自动创建),参考帖子里的教程

离线

楼主 #16 2021-12-23 14:32:47

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

Re: 全志点灯LED

sunxiang 说:

这个如何编译到内核里面有没有例子呀,好期待哦

已更新,请查收

离线

#17 2021-12-23 19:13:18

kwongwo
会员
注册时间: 2020-11-17
已发帖子: 37
积分: 19

Re: 全志点灯LED

该评论内容与本帖子无关,鼓励各位坑友积极发言讨论与帖子有关的内容!

离线

  • 不通过:其他

#18 2021-12-23 23:42:23

海石生风
会员
所在地: 深圳
注册时间: 2019-07-02
已发帖子: 649
积分: 782
个人网站

Re: 全志点灯LED

内核版本在4.8及以上的还可以用新出的libgpiod即gpio的字符设备驱动,libgpiod项目还附带了几个用户空间工具:gpiodetect、gpioinfo、gpioset等

离线

#19 2021-12-24 19:01:45

CHSHIQING
会员
注册时间: 2020-11-27
已发帖子: 47
积分: 2

Re: 全志点灯LED

该评论内容与本帖子无关,鼓励各位坑友积极发言讨论与帖子有关的内容!

离线

  • 不通过:其他

#20 2021-12-25 10:00:13

sunxiang
会员
注册时间: 2021-05-05
已发帖子: 222
积分: 121

Re: 全志点灯LED

资本家大善人 说:
sunxiang 说:

这个如何编译到内核里面有没有例子呀,好期待哦

已更新,请查收

这样就从寄存器到应用程序全部讲清楚了,给大善点赞,这个才是linux入门的好帖子。

离线

#21 2021-12-26 16:49:42

axxx
会员
注册时间: 2021-08-31
已发帖子: 12
积分: 5.5

Re: 全志点灯LED

该评论内容与本帖子无关,鼓励各位坑友积极发言讨论与帖子有关的内容!

离线

  • 不通过:其他

#22 2021-12-26 23:25:26

sunxiang
会员
注册时间: 2021-05-05
已发帖子: 222
积分: 121

Re: 全志点灯LED

sunxiang 说:
资本家大善人 说:
sunxiang 说:

这个如何编译到内核里面有没有例子呀,好期待哦

已更新,请查收

这样就从寄存器到应用程序全部讲清楚了,给大善点赞,这个才是linux入门的好帖子。

已经将led编译到内核里啦,然后每次上电就添加一次节点,然后自动执行脚本,led闪烁起来了。这样就好像又回到了单片机,哈哈。

离线

楼主 #23 2021-12-27 00:39:17

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

Re: 全志点灯LED

@sunxiang
驱动程序里可以自动生成设备文件,网上找找例程,改改,4楼里也有

最近编辑记录 资本家大善人 (2021-12-27 00:40:17)

离线

#24 2021-12-27 10:39:22

sunxiang
会员
注册时间: 2021-05-05
已发帖子: 222
积分: 121

Re: 全志点灯LED

V3S_LED_TREE.png
就是大善的这个参考资料,直接这样改吗,不需要 gpios= <&pio 1 5 GPIO_ACTIVE_LOW>;这一行吗,  status=“okay”这个也没有提到哪个io呀,我有点懵。

离线

#25 2021-12-29 19:06:44

sunxiang
会员
注册时间: 2021-05-05
已发帖子: 222
积分: 121

Re: 全志点灯LED

点灯就是开始,接下来timer,pwm,spi,adc,exit,sdio,usb,,,,这样就慢慢入门啦

最近编辑记录 sunxiang (2021-12-29 19:08:06)

离线

#26 2021-12-31 21:48:38

yelong98
会员
注册时间: 2020-06-03
已发帖子: 97
积分: 59.5

Re: 全志点灯LED

大善人这个资料真的不错,这是真真的入门好贴,感谢分享

离线

#27 2022-02-20 13:37:50

mysteryli
会员
注册时间: 2020-03-05
已发帖子: 485
积分: 390
个人网站

Re: 全志点灯LED

@资本家大善人
老哥 你3楼的那个方法,适用于V3S嘛
另外没有看出来你那个点亮的哪个gpio呀怎么更改要设置的gpio呀

离线

#28 2022-02-20 22:05:02

yelong98
会员
注册时间: 2020-06-03
已发帖子: 97
积分: 59.5

Re: 全志点灯LED

大善人赶紧发布下一阶段的教程啊,pwm spi  串口 usb,嗷嗷待哺

离线

#29 2022-02-21 12:06:15

xiaoxiao315
会员
注册时间: 2022-02-21
已发帖子: 1
积分: 1

Re: 全志点灯LED

坐等后续其他外设

离线

楼主 #30 2022-02-21 16:35:34

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

Re: 全志点灯LED

mysteryli 说:

@资本家大善人
老哥 你3楼的那个方法,适用于V3S嘛
另外没有看出来你那个点亮的哪个gpio呀怎么更改要设置的gpio呀

都试用啊

离线

楼主 #31 2022-02-21 16:36:24

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

Re: 全志点灯LED

yelong98 说:

大善人赶紧发布下一阶段的教程啊,pwm spi  串口 usb,嗷嗷待哺

pwm出了啊,其他的没引出外设

离线

#32 2022-07-08 10:21:41

liyucai
会员
注册时间: 2019-12-06
已发帖子: 45
积分: 22

Re: 全志点灯LED

gpio 驱动可能是 linux 下最特异的驱动,每个应用的需求都不一样,而每个应用都会用到。

linux 下有没有通用的 gpio 驱动?

离线

#34 2022-07-15 10:32:38

oldfox126
会员
注册时间: 2022-07-15
已发帖子: 5
积分: 0

Re: 全志点灯LED

单片机上点灯就非常简单,在LINUX下面这么复杂。

离线

#40 2023-03-09 01:12:12

GGwut
会员
注册时间: 2023-03-09
已发帖子: 7
积分: 7

Re: 全志点灯LED

应该以后能够参考,先收藏,希望用到时还能找到

离线

#41 2023-03-21 09:52:29

Aryeduino
会员
注册时间: 2023-03-21
已发帖子: 12
积分: 7

Re: 全志点灯LED

I would be happy if you can assit with that on tinaLinux with F133 board MQ-R i have - GPIO setting.

离线

#43 2023-05-07 23:08:42

yuan
会员
注册时间: 2023-05-07
已发帖子: 14
积分: 9

Re: 全志点灯LED

很有助于学习Linux内核

离线

#44 2024-05-12 11:12:57

sxlyq_666
会员
注册时间: 2023-09-07
已发帖子: 76
积分: 72

Re: 全志点灯LED

#define N (PIN_N % 8 * 4)   //引脚x : x % 8 * 4

请问这个引脚怎么得来的? 比如我用的是PG6引脚,请问PIN_N应该怎么设置?

离线

楼主 #45 2024-05-12 13:51:37

资本家大善人
会员
注册时间: 2021-03-26
已发帖子: 190
积分: 140.5

Re: 全志点灯LED

sxlyq_666 说:

#define N (PIN_N % 8 * 4)   //引脚x : x % 8 * 4

请问这个引脚怎么得来的? 比如我用的是PG6引脚,请问PIN_N应该怎么设置?


#define PB_CFG0_REG 0x01C20800 + 1 * 0x24 //PB配置寄存器 A:0 B:1 C:2 ....
#define PB_DATA_REG 0x01C20800 + 1 * 0x34 //PB数据寄存器 A:0 B:1 C:2 ....
#define PIN_N 5 //第5个引脚


PG6
==>

#define PG_CFG0_REG 0x01C20800 + 6 * 0x24 //PG配置寄存器 A:0 B:1 C:2 D3 E4 F5 G6 ....
#define PG_DATA_REG 0x01C20800 + 6 * 0x34 //PG数据寄存器 A:0 B:1 C:2 ....
#define PIN_N 6 //第6个引脚

离线

#46 2024-05-12 22:06:37

sxlyq_666
会员
注册时间: 2023-09-07
已发帖子: 76
积分: 72

Re: 全志点灯LED

@资本家大善人
楼主确实是大善人。
我对楼主的程序稍微做了些修改。

我的开发板是Nano Neo,我用的是PG11的引脚,但是寄存器计算公式和楼主代码不一致,我没有计算,我直接用了基址寄存器地址+偏移量。因为PG11根据公式,计算出来的寄存器地址是第0个配置寄存器地址,而PG11要通过第1个配置寄存器进行配置。

感谢楼主。

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn