硬件知識 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 狀態(tài),而無需關注GPIO 引腳輸出的是 3.3V 還是 1.2V。所以簡稱輸出 1 或 0:
GPIO 引腳操作方法
GPIO 模塊一般結構
- 有多組 GPIO,每組有多個 GPIO
- 使能:電源/時鐘
- 模式(Mode):引腳可用于 GPIO 或串口或其他功能
- 方向:引腳 Mode 設置為 GPIO 時,可以繼續(xù)設置它是輸出引腳,還是輸入引腳
-
數(shù)值:
- 對于輸出引腳,可以設置寄存器讓它輸出高、低電平
- 對于輸入引腳,可以讀取寄存器得到引腳的當前電平
GPIO 寄存器的一般操作
芯片手冊一般有相關章節(jié),用來介紹:power/clock
- 可以設置對應寄存器使能某個 GPIO 模塊(Module)
- 有些芯片的 GPIO 是沒有使能開關的,即它總是使能的
一個引腳可以用于 GPIO、串口、USB 或其他功能,
- 有對應的寄存器來選擇引腳的功能
對于已經設置為 GPIO 功能的引腳,有方向寄存器用來設置它的方向:輸出、輸入
對于已經設置為 GPIO 功能的引腳,有數(shù)據寄存器用來寫、讀引腳電平狀態(tài);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,我們需要使能鎖相環(huán)和外設 GPIO 各自對應的時鐘。設置 RCC_PLL4CR 使能 hclk4 使用的時鐘
RCC_PLL4CR地址:0x50000000 + 0x894
還需要讀取bit 1 ,查看PLL4是否使能
MPU、MCU共享GPIO模塊
對于A7、M4而言,GPIO模塊是公用的,寄存器的操作也是類似的
1. 在MPU上使能某個GPIO模塊
2. 在MCU上使能某個GPIO模塊
GPIO模塊
GPIO引腳電路原理圖:
對于 STM32MP157 來說,每一個 GPIO 端口有四個 32 位的配置寄存器(GPIOx_MODER, GPIOx_OTYPER, GPIOx_OSPEEDR 和 GPIOx_PUPDR),兩個32 位的數(shù)據寄存器(GPIOx_IDR 和 GPIOx_ODR),一個 32 位的設置/復位寄存器(GPIOx_BSRR)。此外,所有的 GPIO 都有一個 32 位的鎖定寄存器(GPIOx_LCKR)和兩個 32 位的多功能選擇寄存器(GPIOx_AFRH andGPIOx_AFRL)。此外,還有 GPIO 外設時鐘控制寄存器。
設置引腳工作模式:GPIO模式
對于輸出引腳:設置輸出類型
對于輸出引腳:設置輸出速度
輸出速度越快越容易對其他外設造成影響
對于輸入/輸出引腳:設置上下拉電阻
對于輸入/輸出引腳:讀取引腳電平
對于輸出引腳:設置引腳電平,方法1
對于輸出引腳:設置引腳電平,方法2
該方法部分芯片支持,需要閱讀芯片手冊
STM32MP157的LED操作方法
LED的操作主要分為兩個部分:
- 查看開發(fā)板內部的LED原理圖(引腳),GPIOA的基地址
- 查看開發(fā)板GPIO的操作方法
所以,打開原理圖,該開發(fā)板提供兩個LED模塊
本次實驗使用到的 GPIOA 和 GPIOG 的基地址
根據上一章中GPIO操作的方法,操作指定寄存器,完成指定功能
- 先使能PLL4:RCC_PLL4CR地址:0x50000000 + 0x894
- 使能GPIOA:RCC_MP_AHB4ENSETR地址:0x50000000 + 0xA28(或RCC_MC_AHB4ENSETR地址:0x50000000 + 0xAA8)
- 設置PA10,用作輸出:GPIOA_MODER地址:0x50002000 + 0x00,設置bit[21:20]=0b01
- 設置PA10的輸出電平:
- 方法一:讀寄存、修改值、寫回去(低效):GPIOA_ODR地址: 0x50002000 + 0x14
- 方法二:直接寫寄存器,一次操作即可,高效GPIOA_BSRR地址: 0x50002000 + 0x18
STM32MP157點亮LED燈
功能:
- 實現(xiàn) led_open 函數(shù),在里面初始化 LED 引腳。
- 實現(xiàn) led_write 函數(shù),在里面根據 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,
};
/* 入口函數(shù) */
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. 使用不同的開發(fā)板內核時, 一定要修改KERN_DIR
# 2. KERN_DIR中的內核要事先配置、編譯, 為了能編譯內核, 要先設置下列環(huán)境變量:
# 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
# 注意: 不同的開發(fā)板不同的編譯器上述3個環(huán)境變量不一定相同,
# 請參考各開發(fā)板的高級用戶使用手冊
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原理可以查看博文:驅動程序——字符設備驅動框架文章來源:http://www.zghlxwxcb.cn/news/detail-772876.html
編譯測試
在Makefile文件目錄下執(zhí)行make指令,此時,目錄下有編譯好的內核模塊hello_drv.ko和可執(zhí)行程序hello_drv_test,移植到開發(fā)板上文章來源地址http://www.zghlxwxcb.cn/news/detail-772876.html
insmod /mnt/led_drv.ko //掛載驅動程序
echo none > /sys/class/leds/heartbeat/trigger // 關閉心跳燈
./ledtest /dev/myled on // 點燈
./ledtest /dev/myled off
到了這里,關于STM32MP157驅動開發(fā)——LED驅動(原始架構)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!