目前工作上需要处理理解这个东西,故放在这里增强记忆。
The AT24C01C/AT24C02C is delivered with the EEPROM array set to logic ‘1’, resulting in FFh data in all locations.
这句话的意思是, 初始化EEPROM的值为 每一个寄存器 为 FF
大部分参考这个内容 i2c目前多集中于单片机内部的通信,用于连接微处理器与外围设备,是PHILIPS串行总线。一般i2c总线由SDA和SCL两条线构成,其中
这个命令是检测i2c设备.
本篇是第一篇接触如此高级议题的c++的文章,让我们来看一下c++的lambda的表达式。
lambda是一个新鲜事物(相比c98),其实到c++ 14这个东西也在不停的发展,还是有一些意思的,至于为什么引入lambda表达式,我目前也没有搞明白,如果后面有一个深入的理解,我再回来补充完善。
(浅显的理解就是,lambda用作为捕捉,就是以一种全新的方式用作简化表达的方式,新的语法。但是优势在哪里,目前不知道) c++给人的感觉就是不停的自我革命,前一版的限制,下一版出来一个特性突破上次的限制。
根据官方文档, 记录点自己的心得,简单的概念理解,可以看这篇文章。
一般的函数定义为:
ret_value function_name(parameter) option { function_body; }
而lambda表达式的形式为:
[ capture ] ( parameter ) option -> return_type { body; };
看到这个箭头,我就突然想起了Rust中,也有类似的用法。
捕捉在形式上利用”,”分割,当然也有一些默认的捕捉符。
[]
不捕获任何变量[&]
以引用的方式捕获外部作用域的所有变量[=]
以赋值方式捕获外部作用域的所有变量[=, &foo]
以赋值的方式捕获外部作用域所有变量,以引用方式捕获foo变量[bar]
以赋值方式捕捉bar变量,不捕获其他变量[this]
捕获当前类的this指针,让lambda表达式拥有和当前类成员同样的访问权限,可以修改类的成员变量,使用类的成员函数。如果已经使用了&或者=,就默认添加此选项。以下几点为特殊说明:
A lambda expression can use a variable without capturing it if the variable
1. is a non-local variable or has static or thread local storage duration (in which case the variable cannot be captured), or
2. is a reference that has been initialized with a constant expression.
A lambda expression can read the value of a variable without capturing it if the variable
1. has const non-volatile integral or enumeration type and has been initialized with a constant expression, or
2. is constexpr and has no mutable members.
符号“()”可以作为他的默认构造函数,看下面的例子:
// generic lambda, operator() is a template with two parameters
auto glambda = [](auto a, auto&& b) { return a < b; };
bool b = glambda(3, 3.14); // ok
返回值为auto
类型的lambda值,可以被称为generic lambda
,从这个简单的例子看的出来,这和模板(template)没有太多的区别。
一个综合的例子:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main(){
std::vector<int> c = {1, 2, 3, 4, 5, 6, 7,8};
int x = 5;
c.erase(std::remove_if(c.begin(), c.end(), [x](int n) { return n < x;}), c.end());
std::cout << "c" << endl;
std::for_each(c.begin(), c.end(), [](int i){ std::cout << i << " "; });
}
何谓c++的基础算法? 就是那些基本的算法,比如 reverse, is_sorted, is_unique等。这些usage在一些场景下使用的比较多。
std::is_sorted 用来判断一个容器是否为一个已排序的容器,比如[1,2.3],然后有一个不满足就会return false;
vector<int> ans = [1,2,3];
if(std::is_sorted(std::begin(res), std::end(res)))
return true;
具体的实现可能为:
template<class ForwardIt>
bool is_sorted(ForwardIt first, ForwardIt last)
{
return std::is_sorted_until(first, last) == last;
}
这样的用法还是挺多的。
根据这篇文章,
std::unique(begin(nums), end(nums)) != end(nums)
说实在的,这个用法还是搞蒙我了。
一个可能的实现就是:
template<class ForwardIt>
ForwardIt unique(ForwardIt first, ForwardIt last)
{
if (first == last)
return last;
ForwardIt result = first;
while (++first != last) {
if (!(*result == *first) && ++result != first) {
*result = std::move(*first);
}
}
return ++result;
}
最近一段时间打算跟着raspberry做一些实验。
The red lead should be connected to 5V if you want to power via the cable, see
below for details
The black lead to GND (3rd pin down)
The white lead to TXD on the Pi (4th pin down)
The green lead to RXD on the pI (5th pin down)
3 - GND
9 - TXD
10 - RXD
华为云 这里有一系列文章介绍的设计模式,但是是通过c语言为载体的,暂时记录在这里。
终于有机会做kernel相关的东西了,暂时先积累,后面再进行总结。
针对kernel,从bootloader开始,内核需要建立虚拟地址或者打印一些log,但是,需要去哪呢?现在大部分需要写到寄存器中,但是地址在哪,如何告知?当然,我可以写死,但是,如果换个PC呢?
ARM之前就是这么干的,然后被linus骂的狗血喷头。
设备树重要的三点是: interrupts address and custom variables
git clone git://git.kernel.org/pub/scm/utils/dtc/dtc.git dtc
$ cd dtc
$ make
具体使用命令:
scripts/dtc/dtc -I dts -O dtb -o /path/to/my-tree.dtb /path/to/my-tree.dts
DTS的编译器dtc可以编译产生:
make ARCH=arm digilent_zed_defconfig
make ARCH=arm scripts // 产生DTC
反编译回去可以使用
scripts/dtc/dtc -I dtb -O dts -o /path/to/fromdtb.dts /path/to/booted_with_this.dtb
/dts-v1/;
/ { # tree's root'
#address-cells = <1>;
#size-cells = <1>;
compatible = "xlnx,zynq-zed";
interrupt-parent = <&gic>;
model = "Xillinux for Zedboard";
aliases {
serial0 = &ps7_uart_1;
} ;
chosen {
bootargs = "consoleblank=0 root=/dev/mmcblk0p2 rw rootwait earlyprintk";
linux,stdout-path = "/axi@0/uart@E0001000";
};
cpus {
[ ... CPU definitions ... ]
} ;
ps7_ddr_0: memory@0 {
device_type = "memory";
reg = < 0x0 0x20000000 >;
} ;
ps7_axi_interconnect_0: axi@0 { # ps7_axi_interconnect_0 是label
#address-cells = <1>;
#size-cells = <1>;
compatible = "xlnx,ps7-axi-interconnect-1.00.a", "simple-bus";
ranges ;
[ ... Peripheral definitions ... ]
} ;
} ;
xillybus_0: xillybus@50000000 {
compatible = "xlnx,xillybus-1.00.a";
reg = < 0x50000000 0x1000 >;
interrupts = < 0 59 1 >;
interrupt-parent = <&gic>;
xlnx,max-burst-len = <0x10>;
xlnx,native-data-width = <0x20>;
xlnx,slv-awidth = <0x20>;
xlnx,slv-dwidth = <0x20>;
xlnx,use-wstrb = <0x1>;
} ;
假设probe函数如下:
static int __devinit xilly_drv_probe(struct platform_device *op)
{
const struct of_device_id *match;
match = of_match_device(xillybus_of_match, &op->dev);
if (!match)
return -EINVAL;
Next, the memory segment is allocated and mapped into virtual memory:
int rc = 0;
struct resource res;
void *registers;
rc = of_address_to_resource(&op->dev.of_node, 0, &res);
if (rc) {
/* Fail */
}
if (!request_mem_region(res.start, resource_size(&res), "xillybus")) {
/* Fail */
}
registers = of_iomap(op->dev.of_node, 0);
if (!registers) {
/* Fail */
}
of_address_to_resource这个函数是可以通过设备树把 resource res这个结构体赋值的。在这个例子中,reg = < 0x50000000 0x1000 >
意味着这块内存地址分配在0x50000000开始,大小为0x1000的地方。of_address_to_resource() will therefore set res.start = 0x50000000 and res.end = 0x50000fff.
接下来的request_mem_region()则是准备注册这个内存地址, 只要有request,就差不多有锁的机制进行保护。
of_iomap() 是of_address_to_resource() and ioremap()函数的组合,这个确保了物理内存段与虚拟内存之间的映射,并返回虚拟内存的地址。
操纵这些数据的最好方式是使用iowrite32() 或者 ioread32()
在驱动侧:
irq = irq_of_parse_and_map(op->dev.of_node, 0);
rc = request_irq(irq, xillybus_isr, 0, "xillybus", op->dev);
irq_of_parse_and_map()会寻找设备树中的中断, request_irq也会在 /proc/interrupt中匹配对应的中断。第二个参数0, 表示前面的中断应该从设备树中取得。
看一下设备树中的中断:
interrupts = < 0 59 1 >;
interrupt-parent = <&gic>;
前面的第一个0暗示这个中断是否为一个SPI(Shared peripheral interrupt). 一个非0值意味着它是SPI. 第二个则为中断号。
xlnx,slv-awidth = <0x20>;
比如,这个代码,就是最简单的将一个具体值赋给外围设备 entry.在驱动侧的代码则是:
void *ptr;
ptr = of_get_property(op->dev.of_node, "xlnx,slv-awidth", NULL);
if (!ptr) {
/* Couldn't find the entry */
}
第三个参数,NULL,可能是一个指向int型的指针,表明已经写入数据的长度。如果想要得到具体的数值,则需要
int value;
value = be32_to_cpup(ptr);
The be32_to_cpup reads the data from the address given by “ptr”, converts from Big Endian to the processor’s native Little Endian, so “value” will contain the integer one expects it to contain.
There are plenty of other API functions for reading arrays etc. See drivers/of/base.c in the Linux sources.
All in all, setting up a device tree entry for a custom IP peripheral is quite simple:
V4L2的实现可以参阅 https://linuxtv.org/downloads/v4l-dvb-apis/
static struct camera_common_pdata *cs_imx307_parse_dt(struct tegracam_device *tc_dev)
{
struct device *dev = tc_dev->dev;
struct device_node *np = dev->of_node;
struct camera_common_pdata *board_priv_pdata;
const struct of_device_id *match;
//struct camera_common_pdata *ret = NULL;
int err;
//int gpio;
match = of_match_device(cs_imx307_of_match, dev);
if (!match) {
dev_err(dev, "Failed to find matching dt id\n");
return NULL;
}
board_priv_pdata = devm_kzalloc(dev,
sizeof(*board_priv_pdata), GFP_KERNEL);
if (!board_priv_pdata)
return NULL;
从这个名字看,应该是这个device首先parse 设备树的某个文件,但是参数中的tc_dev是如何生成的,这也是一个有趣的问题。重点现在没有找到of_match_device
的机制是如何的
这里有一个很好的ov 其中的答案中的链接非常有用,需要接下来进行更深入的了解。
具体的用法,请参考这个
在第一节中,我引用了别人的说法,正所谓,不是自己得出来的就不是自己的。 按照lwn的说法,目前的linux系统很容易在一个新的系统启动.需要做两件事:
描述设备
在启动阶段注册所有的设备。
但是呢,这样的方法也是非常的不方便。前面是集中在板级文件,目前的方案就是设备树,让内核自己去侦测应该启动内核的哪一部分。
出现在设备树中的设备名(在“compatible”属性中)往往采用标准化的形式,不一定与Linux内核中驱动程序的名称相匹配;在其他方面,设备树确实意味着可以与多个操作系统一起工作。因此,最好将特定的名称附加到平台设备上,以便与设备树一起使用。内核提供了一个of_device_id结构,可以用于这个目的:
static const struct of_device_id my_of_ids[] = {
{ .compatible = "long,funky-device-tree-name" },
{ }
};
设备树中,最重要的项就是”compatible”.
When the kernel starts up, it kicks off compiled-in drivers that match “compatible” entries it finds in the device tree
At a later stage (when /lib/modules is available), all kernel modules that match “compatible” entries in the device tree are loaded.
static struct of_device_id xillybus_of_match[] __devinitdata = {
{ .compatible = "xlnx,xillybus-1.00.a", },
{} // 可以加载多个 compatible
};
MODULE_DEVICE_TABLE(of, xillybus_of_match);
of_device_id是连接内核 driver与campatible之间的桥梁
static struct platform_driver xillybus_platform_driver = {
.probe = xilly_drv_probe,
.remove = xilly_drv_remove,
.driver = {
.name = "xillybus",
.owner = THIS_MODULE,
.of_match_table = xillybus_of_match,
},
};
然后xillybus_platform_driver必须被调用在模块初始化函数中。
An important thing to note when writing kernel modules, is that the automatic loading mechanism (modprobe, actually) depends on an entry for the “compatible” string in /lib/modules/{kernel version}/modules.ofmap and other definition files in the same directory. The correct way to make this happen for your own module, is to copy the *.ko file to somewhere under the relevant /lib/modules/{kernel version}/kernel/drivers/ directory and go
# depmod -a
on the target platform, with that certain kernel version loaded (or define which kernel version to depmod).
这块是我之前没有涉及到的地方,可以在这里入手。
1、概述:
通常在Linux中,把SoC系统中集成的独立外设单元(如:I2C、IIS、RTC、看门狗等)都被当作平台设备来处理。
从Linux2.6起,引入了一套新的驱动管理和注册机制:Platform_device和Platform_driver,来管理相应设备。
Linux中大部分的设备驱动,都可以使用这套机制,设备用platform_device表示,驱动用platform_driver进行注册。
Linux platform driver机制和传统的device_driver机制相比,一个十分明显的优势在于platform机制将本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform_device提供的标准接口进行申请并使用。
这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性。
platform是一个虚拟的地址总线,相比pci,usb,它主要用于描述SOC上的片上资源,比如s3c2410上集成的控制器(lcd,watchdog,rtc等),platform所描述的资源有一个共同点,就是可以在cpu的总线上直接取址。
2、platform机制分为三个步骤 1)总线注册阶段: 内核启动初始化时的main.c文件中的 kernel_init() ->do_basic_setup() -> driver_init() ->platform_bus_init() ->bus_register(&platform_bus_type),注册了一条platform总线(虚拟总线) platform总线用于连接各类采用platform机制的设备,此阶段无需我们修改,由linux内核维护。 2)添加设备阶段: 设备注册的时候 Platform_device_register() -> platform_device_add() -> pdev->dev.bus = &platform_bus_type -> device_add(),就这样把设备给挂到虚拟的总线上。 本阶段是增加设备到plartform总线上,我们增加硬件设备,需要修改此处信息。 此部分操作一般arm/arm/mach-s3c2440/mach-smdk2440.c类似的文件中,需要我们根据硬件的实际需要修改相应代码 3)驱动注册阶段: Platform_driver_register() ->driver_register() -> bus_add_driver() -> driver_attach() ->bus_for_each_dev(), 对在每个挂在虚拟的platform bus的设备作__driver_attach() ->driver_probe_device() 判断drv->bus->match()是否执行成功,此时通过指针执行platform_match-> strncmp(pdev->name , drv->name , BUS_ID_SIZE),如果相符就调用really_probe(实际就是执行相应设备的platform_driver->probe(platform_device)。) 开始真正的探测,如果probe成功,则绑定设备到该驱动。 本阶段是在编写具体的驱动程序时完成,在注册驱动时获取步骤2中已经申请的资料,一般由程序开发者完成。
========= 引用结束 ====================