一、UBOOT启动流程
开始之前,不得不说UBOOT启动的三个阶段,这个很重要!
第一阶段(汇编语言阶段)
1. 设置异常向量表,当系统发生异常时总会接入到一个固定地址。
2. 硬件初始化。配置时钟相关参数,比如分频系数等等(内核时钟,总线时钟,IO接口时钟)
3. 关闭看门狗。
4. 关闭MMU,这点很重要。
5. 关闭中断。
6. 启动ICACHE(指令),关闭DCACHE(数据)。
7. 为第二阶段的代码准备ram空间。准备ram空间主要就是使内存芯片可用。
8. 复制uboot第二阶段的代码至ram中。
9. 设置pc指针指向start_armboot函数,为第二阶段代码的入口(同时需要清除BSS段)。
第二阶段
1. 初始化外设。
2. 检测内存映射。主要是检测已经使用了的内存空间。
3. 将内核以及根文件系统从flash读取到SDRAM上。
4. 为内核设置启动参数。
第三阶段(重要参数bootcmd以及booargs)
1. bootcmd,保存了uboot的默认命令,uboot倒计时结束后就会执行此命令。
2. bootargs,保存着uboot传递给内核的参数。
在UBOOT开发篇最后,笔者打算介绍下如何从零开发自己一个UBOOT,然后去引导内核启动。
二、UBOOT下GPIO操作
uboot下操作gpio有两种方式,一种为传统方式,一种为DM驱动模型方式。
2.1 传统模式(方式一):
#include <asm/unaligned.h>
#include <config.h>
#include <common.h>
#include <errno.h>
#include <fdtdec.h>
#include <fdt_support.h>
#include <linux/list.h>
#include <linux/compat.h>
#include <linux/media-bus-format.h>
#include <malloc.h>
#include <i2c.h>
#include <dm.h>
#include <dt-bindings/gpio/gpio.h>
#include <asm/gpio.h>
static void rk628_poweron(void)
{
int enable_gpio = 146;
gpio_request(146,"enable"); //很重要,一定要先申请
gpio_direction_output(enable_gpio, 1);
mdelay(10);
}
2.2 传统模式(方式二):
因为uboot节点关闭了MMU,uboot阶段就是物理地址。所以可以对照datasheet可以直接对物理地址操作:
typedef volatile unsigned int u32;
u32 cfg, value;
//step1.set output.
cfg = *(u32 *)(基地址 + 控制寄存器偏移地址);
value = cfg & ~(7 << 4); //控制寄存器设置
value = value | (1 << 4);
*(u32 *)(基地址 + 控制寄存器偏移地址) =value;
//step2.write value
cfg = *(u32 *)(基地址 + 数据寄存器偏移地址);
value = cfg | (1 << 17); //要写入的数据
*(u32 *)(基地址 + 数据寄存器偏移地址) =value;
2.3 DM驱动模型方式:
(示例包含i2c操作与gpio操作)
1. dts文件(dts文件写在kernel中)
&i2c3 {
clock-frequency = <400000>;
status= "okay";
hym8563: hym8563@51 {
compatible = "haoyu,hym8563";
reg = <0x51>;
status = "okay";
enable-gpios = <&gpio0 RK_PB6GPIO_ACTIVE_HIGH>; //此IO仅仅演示GPIO操作
reset-gpios = <&gpio0 RK_PB5GPIO_ACTIVE_LOW>; //此IO仅仅演示GPIO操作
};
};
2. hym8563.h
#ifndef _HYM8563_H_
#define _HYM8563_H_
#include <dm/device.h>
#include <asm/gpio.h>
struct hym8563 {
structudevice *dev;
structgpio_desc enable_gpio;
structgpio_desc reset_gpio;
};
//8位寄存器,8位值
int hym8563_i2c_write_u8(struct hym8563*hym8563, u8 reg, u8 val);
int hym8563_i2c_read_u8(struct hym8563*hym8563, u8 reg, u8 *val);
//16位寄存器,32位值
int hym8563_i2c_write_u32(struct hym8563*hym8563, u16 reg, u32 val);
int hym8563_i2c_read_u32(struct hym8563*hym8563, u16 reg, u32 *val);
#endif
3. hym8563.c文章来源:https://www.toymoban.com/news/detail-530686.html
#include <common.h>
#include <i2c.h>
#include <errno.h>
#include <dm.h>
#include <dm/uclass.h>
#include <dm/uclass-id.h>
#include "hym8563.h"
/********************************************
&i2c3 {
clock-frequency= <400000>;
status= "okay";
hym8563:hym8563@51 {
compatible= "haoyu,hym8563";
reg= <0x51>;
status= "okay";
enable-gpios= <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>;
reset-gpios= <&gpio0 RK_PB5 GPIO_ACTIVE_LOW>;
};
};
********************************************/
//8位寄存器,8位值
int hym8563_i2c_write_u8(struct hym8563*hym8563, u8 reg, u8 val)
{
structdm_i2c_chip *chip = dev_get_parent_platdata(hym8563->dev);
structi2c_msg msg;
u8buf[] = {
(reg>> 0) & 0xff,
(val>> 0) & 0xff
};
u8ret;
msg.addr= chip->chip_addr;
msg.flags= 0;
msg.len= sizeof(buf);
msg.buf= buf;
ret= dm_i2c_xfer(hym8563->dev, &msg, 1);
if(ret) {
dev_err(hym8563->dev,"Could not execute transfer: %d\n", ret);
returnret;
}
return0;
}
int hym8563_i2c_read_u8(struct hym8563*hym8563, u8 reg, u8 *val)
{
structdm_i2c_chip *chip = dev_get_parent_platdata(hym8563->dev);
u8data;
structi2c_msg msg[] = {
{
.addr= chip->chip_addr,
.flags= 0,
.buf= (u8 *)®,
.len= 1,
},{
.addr= chip->chip_addr,
.flags= I2C_M_RD,
.buf= (u8 *)&data,
.len= 1,
}
};
intret;
ret= dm_i2c_xfer(hym8563->dev, msg, 2);
if(ret) {
dev_err(hym8563->dev,"Could not execute transfer: %d\n", ret);
returnret;
}
*val= data;
return0;
}
//16位寄存器,32位值
int hym8563_i2c_write_u32(struct hym8563*hym8563, u16 reg, u32 val)
{
structdm_i2c_chip *chip = dev_get_parent_platdata(hym8563->dev);
structi2c_msg msg;
u8buf[] = {
(reg>> 0) & 0xff, (reg >> 8) & 0xff,
(val>> 0) & 0xff, (val >> 8) & 0xff,
(val>> 16) & 0xff, (val >> 24) & 0xff
};
intret;
msg.addr= chip->chip_addr;
msg.flags= 0;
msg.len= sizeof(buf);
msg.buf= buf;
ret= dm_i2c_xfer(hym8563->dev, &msg, 1);
if(ret) {
dev_err(hym8563->dev,"Could not execute transfer: %d\n", ret);
returnret;
}
return0;
}
int hym8563_i2c_read_u32(struct hym8563*hym8563, u16 reg, u32 *val)
{
structdm_i2c_chip *chip = dev_get_parent_platdata(hym8563->dev);
u32data;
structi2c_msg msg[] = {
{
.addr= chip->chip_addr,
.flags= 0,
.buf= (u8 *)®,
.len= 2,
},{
.addr= chip->chip_addr,
.flags= I2C_M_RD,
.buf= (u8 *)&data,
.len= 4,
}
};
intret;
ret= dm_i2c_xfer(hym8563->dev, msg, 2);
if(ret) {
dev_err(hym8563->dev,"Could not execute transfer: %d\n", ret);
returnret;
}
*val= data;
return0;
}
static int hym8563_probe(struct udevice*dev)
{
structhym8563 *hym8563 = dev_get_priv(dev);
hym8563->dev= dev;
intret, i;
u8val;
//从dts中解析gpio
ret= gpio_request_by_name(dev, "enable-gpios", 0,
&hym8563->enable_gpio, GPIOD_IS_OUT);
if(ret && ret != -ENOENT) {
dev_err(dev,"Cannot get enable GPIO: %d\n", ret);
returnret;
}
//从dts中解析gpio
ret= gpio_request_by_name(dev, "reset-gpios", 0,
&hym8563->reset_gpio, GPIOD_IS_OUT);
if(ret) {
dev_err(dev,"Cannot get reset GPIO: %d\n", ret);
returnret;
}
//设置io口电平
if(dm_gpio_is_valid(&hym8563->enable_gpio))
{
dm_gpio_set_value(&hym8563->enable_gpio,1);
}
dm_gpio_set_value(&hym8563->reset_gpio,1);
//i2c读写
for(i= 0; i < 5; i++)
{
hym8563_i2c_read_u8(hym8563,0x02, &val); //0x02寄存器为rtc芯片的秒计数
printf("reg:0x00=0x%x\n",val);
mdelay(1000);
}
return0;
}
static const struct udevice_idhym8563_of_match[] = {
{.compatible = "haoyu,hym8563" },
{}
};
U_BOOT_DRIVER(hym8563) = {
.name= "hym8563",
.id= UCLASS_I2C_GENERIC,
.of_match= hym8563_of_match,
.probe= hym8563_probe,
.bind= dm_scan_fdt_dev,
.priv_auto_alloc_size= sizeof(struct hym8563),
};
4. main.c文章来源地址https://www.toymoban.com/news/detail-530686.html
#include <common.h>
#include <command.h>
#include <part.h>
#include <dm.h>
#include <asm/gpio.h>
int do_demo(cmd_tbl_t *cmdtp, int flag, intargc, char * const argv[])
{
struct udevice *udev;
int ret;
//uboot DM中,用户必须主动调用框架接口发起 probe,才会进入到hym8563_probe函数
ret =uclass_get_device_by_name(UCLASS_I2C_GENERIC,"hym8563@51",&udev);
return ret;
}
U_BOOT_CMD(
demo, 2, 0, do_demo,
"make demo running.",
NULL
);
到了这里,关于RK3568平台入门到精通系列讲解之UBOOT开发篇(GPIO操作)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!