LED驱动(原始架构)——STM32MP157

这篇具有很好参考价值的文章主要介绍了LED驱动(原始架构)——STM32MP157。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

硬件知识 LED 原理

LED 的驱动方式,常见的有四种。

  • ① 使用引脚输出 3.3V 点亮 LED,输出 0V 熄灭 LED。
  • ② 使用引脚拉低到 0V 点亮 LED,输出 3.3V 熄灭 LED。
  • ③ 使用引脚输出 1.2V 点亮 LED,输出 0V 熄灭 LED。
  • ④ 使用引脚输出 0V 点亮 LED,输出 1.2V 熄灭 LED。

有的芯片为了省电等原因,其引脚驱动能力不足,这时可以使用三极管驱动(如方式3和4)。
LED驱动(原始架构)——STM32MP157,架构,stm32,单片机
由此,主芯片引脚输出高电平/低电平,即可改变 LED 状态,而无需关注GPIO 引脚输出的是 3.3V 还是 1.2V。所以简称输出 1 或 0:

GPIO 引脚操作方法

GPIO 模块一般结构

  • 有多组 GPIO,每组有多个 GPIO
  • 使能:电源/时钟
  • 模式(Mode):引脚可用于 GPIO 或串口或其他功能
  • 方向:引脚 Mode 设置为 GPIO 时,可以继续设置它是输出引脚,还是输入引脚
  • 数值
    • 对于输出引脚,可以设置寄存器让它输出高、低电平
    • 对于输入引脚,可以读取寄存器得到引脚的当前电平

GPIO 寄存器的一般操作

芯片手册一般有相关章节,用来介绍:power/clock

  • 可以设置对应寄存器使能某个 GPIO 模块(Module)
  • 有些芯片的 GPIO 是没有使能开关的,即它总是使能的

一个引脚可以用于 GPIO、串口、USB 或其他功能,

  • 有对应的寄存器来选择引脚的功能

对于已经设置为 GPIO 功能的引脚,有方向寄存器用来设置它的方向:输出、输入

对于已经设置为 GPIO 功能的引脚,有数据寄存器用来写、读引脚电平状态;GPIO 寄存器的 2 种操作方法:

  • 直接读写:读出、修改对应位、写入
a) 要设置 bit n:
val = data_reg;
val = val | (1<<n);
data_reg = val;

b) 要清除 bit n:
val = data_reg;
val = val & ~(1<<n);
data_reg = val;
  • set-and-clear protocol:
set_reg, clr_reg, data_reg 三个寄存器对应的是同一个物理寄存器,
a) 要设置 bit n:set_reg = (1<<n);
b) 要清除 bit n:clr_reg = (1<<n);//这里的置1表示该位清零,硬件内部会操作

STM32MP157的GPIO操作方法

先使能PLL4

PLL4用于给各种外设提供时钟,最先要使能PLL4;

GPIO是低速设备,我们可以先不去设置PLL4的频率;仅仅使能即可

GPIO 外设的时钟来源各自不同,具体的可以从手册当中可以看到,其中 GPIOA-K 的时钟来源为 hclk4,GPIOZ 的时钟来源为 hclk5。因此为了使用 GPIO,我们需要使能锁相环和外设 GPIO 各自对应的时钟。设置 RCC_PLL4CR 使能 hclk4 使用的时钟

RCC_PLL4CR地址:0x50000000 + 0x894

LED驱动(原始架构)——STM32MP157,架构,stm32,单片机
还需要读取bit 1 ,查看PLL4是否使能

MPU、MCU共享GPIO模块

对于A7、M4而言,GPIO模块是公用的,寄存器的操作也是类似的
LED驱动(原始架构)——STM32MP157,架构,stm32,单片机

1. 在MPU上使能某个GPIO模块

LED驱动(原始架构)——STM32MP157,架构,stm32,单片机

2. 在MCU上使能某个GPIO模块

LED驱动(原始架构)——STM32MP157,架构,stm32,单片机

GPIO模块

GPIO引脚电路原理图:
LED驱动(原始架构)——STM32MP157,架构,stm32,单片机
对于 STM32MP157 来说,每一个 GPIO 端口有四个 32 位的配置寄存器(GPIOx_MODER, GPIOx_OTYPER, GPIOx_OSPEEDR 和 GPIOx_PUPDR),两个32 位的数据寄存器(GPIOx_IDR 和 GPIOx_ODR),一个 32 位的设置/复位寄存器(GPIOx_BSRR)。此外,所有的 GPIO 都有一个 32 位的锁定寄存器(GPIOx_LCKR)和两个 32 位的多功能选择寄存器(GPIOx_AFRH andGPIOx_AFRL)。此外,还有 GPIO 外设时钟控制寄存器。

设置引脚工作模式:GPIO模式

LED驱动(原始架构)——STM32MP157,架构,stm32,单片机

对于输出引脚:设置输出类型

LED驱动(原始架构)——STM32MP157,架构,stm32,单片机

对于输出引脚:设置输出速度

LED驱动(原始架构)——STM32MP157,架构,stm32,单片机
输出速度越快越容易对其他外设造成影响

对于输入/输出引脚:设置上下拉电阻

LED驱动(原始架构)——STM32MP157,架构,stm32,单片机

对于输入/输出引脚:读取引脚电平

LED驱动(原始架构)——STM32MP157,架构,stm32,单片机

对于输出引脚:设置引脚电平,方法1

LED驱动(原始架构)——STM32MP157,架构,stm32,单片机

对于输出引脚:设置引脚电平,方法2

该方法部分芯片支持,需要阅读芯片手册
LED驱动(原始架构)——STM32MP157,架构,stm32,单片机

STM32MP157的LED操作方法

LED的操作主要分为两个部分:

  1. 查看开发板内部的LED原理图(引脚),GPIOA的基地址
  2. 查看开发板GPIO的操作方法

所以,打开原理图,该开发板提供两个LED模块
LED驱动(原始架构)——STM32MP157,架构,stm32,单片机
本次实验使用到的 GPIOA 和 GPIOG 的基地址
LED驱动(原始架构)——STM32MP157,架构,stm32,单片机

根据上一章中GPIO操作的方法,操作指定寄存器,完成指定功能

  1. 先使能PLL4:RCC_PLL4CR地址:0x50000000 + 0x894
  2. 使能GPIOA:RCC_MP_AHB4ENSETR地址:0x50000000 + 0xA28(或RCC_MC_AHB4ENSETR地址:0x50000000 + 0xAA8)
  3. 设置PA10,用作输出:GPIOA_MODER地址:0x50002000 + 0x00,设置bit[21:20]=0b01
  4. 设置PA10的输出电平:
    1. 方法一:读寄存、修改值、写回去(低效):GPIOA_ODR地址: 0x50002000 + 0x14
    2. 方法二:直接写寄存器,一次操作即可,高效GPIOA_BSRR地址: 0x50002000 + 0x18

STM32MP157点亮LED灯

功能:

  • 实现 led_open 函数,在里面初始化 LED 引脚。
  • 实现 led_write 函数,在里面根据 APP 传来的值控制 LED。

led_drv.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>

static int major;
static struct class *led_class;

/* registers */
// RCC_PLL4CR地址:0x50000000 + 0x894
static volatile unsigned int *RCC_PLL4CR = NULL;

// RCC_MP_AHB4ENSETR 地址:0x50000000 + 0xA28
static volatile unsigned int *RCC_MP_AHB4ENSETR = NULL;

// GPIOA_MODER 地址:0x50002000 + 0x00
static volatile unsigned int *GPIOA_MODER = NULL;

// GPIOA_BSRR 地址: 0x50002000 + 0x18
static volatile unsigned int *GPIOA_BSRR = NULL;

static ssize_t led_write(struct file *filp, const char __user *buf,
			 size_t count, loff_t *ppos)
{
	char val;
	/* copy_from_user : get data from app */
	copy_from_user(&val, buf, 1);

	/* to set gpio register: out 1/0 */
	if (val)
	{
		/* set gpa10 to let led on */
		*GPIOA_BSRR = (1<<26);
	}
	else
	{

		/* set gpa10 to let led off */
		*GPIOA_BSRR = (1<<10);
	}
	return 1;
}

static int led_open(struct inode *inode, struct file *filp)
{
	/* enalbe PLL4, it is clock source for all gpio */
	*RCC_PLL4CR |= (1<<0);
	while ((*RCC_PLL4CR & (1<<1)) == 0);
	
	/* enable gpioA */
	*RCC_MP_AHB4ENSETR |= (1<<0);
	
	/*
	 * configure gpa10 as gpio
	 * configure gpio as output 
	 */
	*GPIOA_MODER &= ~(3<<20);//清零
	*GPIOA_MODER |= (1<<20);

	return 0;
}

static struct file_operations led_fops = {
	.owner		= THIS_MODULE,
	.write		= led_write,
	.open		= led_open,
};

/* 入口函数 */
static int __init led_init(void)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	major = register_chrdev(0, "my_led", &led_fops);

	/* ioremap(base_phy, size); */
	// RCC_PLL4CR地址:0x50000000 + 0x894
	RCC_PLL4CR =  (volatile unsigned int *)ioremap(0x50000000 + 0x894, 4);
	
	// RCC_MP_AHB4ENSETR 地址:0x50000000 + 0xA28
	RCC_MP_AHB4ENSETR =  (volatile unsigned int *)ioremap(0x50000000 + 0xA28, 4);
	
	// GPIOA_MODER 地址:0x50002000 + 0x00
	GPIOA_MODER =  (volatile unsigned int *)ioremap(0x50002000 + 0x00, 4);
	
	// GPIOA_BSRR 地址: 0x50002000 + 0x18
	GPIOA_BSRR =  (volatile unsigned int *)ioremap(0x50002000 + 0x18, 4);

	led_class = class_create(THIS_MODULE, "myled");
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "myled"); /* /dev/myled */
	
	return 0;
}

static void __exit led_exit(void)
{
	iounmap(RCC_PLL4CR);
	iounmap(RCC_MP_AHB4ENSETR);
	iounmap(GPIOA_MODER);
	iounmap(GPIOA_BSRR);
	
	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);
	
	unregister_chrdev(major, "my_led");
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

ledtest.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>


// ledtest /dev/myled on
// ledtest /dev/myled off

int main(int argc, char **argv)
{
	int fd;
	char status = 0;
	
	if (argc != 3)
	{
		printf("Usage: %s <dev> <on|off>\n", argv[0]);
		printf("  eg: %s /dev/myled on\n", argv[0]);
		printf("  eg: %s /dev/myled off\n", argv[0]);
		return -1;
	}
	// open
	fd = open(argv[1], O_RDWR);
	if (fd < 0)
	{
		printf("can not open %s\n", argv[0]);
		return -1;
	}

	// write
	if (strcmp(argv[2], "on") == 0)
	{
		status = 1;
	}

	write(fd, &status, 1);
	return 0;	
}

Makefile

# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin 
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
#       请参考各开发板的高级用户使用手册

KERN_DIR = /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o ledtest ledtest.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f ledtest

obj-m	+= led_drv.o

makefile原理可以查看博文:驱动程序——字符设备驱动框架

编译测试

在Makefile文件目录下执行make指令,此时,目录下有编译好的内核模块hello_drv.ko和可执行程序hello_drv_test,移植到开发板上文章来源地址https://www.toymoban.com/news/detail-574955.html

insmod /mnt/led_drv.ko //挂载驱动程序
echo none > /sys/class/leds/heartbeat/trigger // 关闭心跳灯
./ledtest /dev/myled on // 点灯
./ledtest /dev/myled off 

到了这里,关于LED驱动(原始架构)——STM32MP157的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32MP157驱动开发——按键驱动(tasklet)

    阅读Linux 系统中异常与中断可知,Linux 系统对中断处理的演进过程中,实现了中断的扩展:硬件中断、软件中断 硬件中断有:GPIO,网络中断(net),系统滴答中断(tick)等 软件中断有:定时器,tasklet等 内核中的软中断: 该数组里面有个action成员,该成员是个函数,函数会调

    2024年02月14日
    浏览(31)
  • STM32MP157驱动开发——按键驱动(工作队列)

    定时器、下半部 tasklet,它们都是在中断上下文中执行,它们无法休眠。当要处理更复杂的事情时,往往更耗时。这些更耗时的工作放在定时器或是下半部中,会使得系统很卡;并且循环等待某件事情完成也太浪费CPU 资源了。如果使用线程来处理这些耗时的工作,那就可以解

    2024年02月15日
    浏览(26)
  • STM32MP157驱动开发——按键驱动(异步通知)

    Linux 系统中也有很多信号,在 Linux 内核源文件 includeuapiasm-genericsignal.h 中,有很多信号的宏定义: 就 APP 而言,你想处理 SIGIO 信息,那么需要提供信号处理函数,并且要跟 SIGIO 挂钩。这可以通过一个 signal 函数 来“给某个信号注册处理函数”,用法如下: 重点从②开始:

    2024年02月15日
    浏览(36)
  • STM32MP157驱动开发——按键驱动(休眠与唤醒)

    当应用程序必须等待某个事件发生,比如必须等待按键被按下时,可以使用“休眠-唤醒”机制: ① APP 调用 read 等函数试图读取数据,比如读取按键; ② APP 进入内核态,也就是调用驱动中的对应函数,发现有数据则复制到用户空间并马上返回; ③ 如果 APP 在内核态,也就

    2024年02月16日
    浏览(32)
  • STM32MP157驱动开发——按键驱动(线程化处理)

    工作队列是在内核的线程的上下文中执行的 工作队列中有多个 work,前一个 work 没处理完会影响后面的 work。解决方法有如下2种: 比如自己创建一个内核线程,不跟别的 work 在一块。例如存储设备比如 SD/TF采用的就是单独一个线程。 使用线程化的中断处理。中断的处理仍然

    2024年02月16日
    浏览(30)
  • STM32MP157驱动开发——按键驱动(POLL 机制)

    使用休眠-唤醒的方式等待某个事件发生时,有一个缺点:等待的时间可能很久。我们可以加上一个超时时间,这时就可以使用 poll 机制。 ① APP 不知道驱动程序中是否有数据,可以先调用 poll 函数查询一下,poll 函数可以传入超时时间; ② APP 进入内核态, 调用到驱动程序的

    2024年02月15日
    浏览(28)
  • STM32MP157驱动开发——USB设备驱动

    参考文章:【正点原子】I.MX6U嵌入式Linux驱动开发——Linux USB驱动   由于 USB 协议太过庞大和复杂,所以本节只对 STM32MP157 自带的 USB 驱动进行使能和测试。详细的 USB 接口和协议的介绍,可以参考原子哥的资料《USB2.0 协议中文版.pdf》和《USB3.0 协议中文版.pdf》。   USB 全

    2023年04月14日
    浏览(31)
  • STM32MP157驱动开发——按键驱动(定时器)

    定时器涉及函数参考内核源码:includelinuxtimer.h 给定时器的各个参数赋值: 设置定时器 :主要是初始化 timer_list 结构体,设置其中的函数、参数。 a) 向内核添加定时器。timer-expires 表示超时时间。 b) 当超时时间到达,内核就会调用这个函数:timer-function(timer-data)。 修改定时

    2024年02月15日
    浏览(38)
  • STM32MP157驱动开发——Linux LCD驱动(上)

      LCD 是很常用的一个外设,通过 LCD 可以显示图片、界面UI等,提高人机交互的效率。STM32MP1 提供了一个 LTDC 接口用于连接 RGB 接口的液晶屏。本节就来学习如何使用这个接口。   LCD 全称是 Liquid Crystal Display,也就是液晶显示器,是现在最常用到的显示器。网上对于 LCD

    2024年02月08日
    浏览(30)
  • 基于STM32F103ZET6单片机驱动SM16306SJ控制8个LED灯的系统设计

    1.设计要求:使用stm32f103zet6精英开发板  采用SM16306SJ驱动8个LED灯。 2.原理图 3.代码 4.实物效果图 备注:1 硬件上有一个LED灯坏了,所以才亮了7个LED灯            2 一个SM16306SJ最多可以驱动16个LED灯            3 SM16306SJ跟MBI5020程序通用

    2024年04月17日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包