CubieBoard博客-HomeCubieTech公司网站

cubie.cc CubieBoard中文论坛

 找回密码
 立即注册
搜索
热搜: unable
查看: 5889|回复: 9

第二期培训分享一些学习笔记

[复制链接]

3

主题

0

好友

490

积分

中级会员

Rank: 3Rank: 3

贡献
100
金钱
177
威望
100
买家信用
卖家信用
积分
490
发表于 2014-9-24 19:28:02 |显示全部楼层
短短的20多天培训,使我加深了对嵌入式linux的了解,谢谢你们的关照。
整理了一下之前的笔记,分享给大家。
1.嵌入式操作系统简介
1.1作用
文件系统《-应用程序-》进程
内存管理《-        -》设备
网络管理《-

1.2地位
操作系统-》封装硬件层给应用层提供接口,让其调用

linux分为稳定版、开发版、发行版(内核+GNU+桌面)

UNIX-》BSD-》Darwin-》IOS

1.3应用领域
个人桌面、服务器、嵌入式(以应用为中心、以计算机技术为基础、软硬件可以裁减、可满足功能、成本、体积、功耗等标准的严格要求)

2.文件系统的目录结构
数据文件、程序文件、设备文件、网络文件
管理员、普通用户能访问的存储的指令-》bin
管理员能访问的存储的指令-》sbin
放不可以改的程序文件-》usr(头文件)
放可以改的程序文件-》var(锁定文件)
临时文件-》tmp
挂载点-》mnt、media(发行版不同)
本机系统信息-》proc

3.1 shell
作用:用户语言-计算机语言
cat进行编写要在编写的下一退出
ln创建文件链接,相当于快捷文件
grep查找 查着内容 查找所在文件 -n(显示的是第几行)(命令)

3.2 文件权限
drwxr-xr-x   marquis marquis  d表示为目录,l表示为链接,rwx为marquis权限 r-x为当前用户marquis权限,r-x为其它用户权限

chown更改哪些用户类型可以执行 chmod更改文件权限
tar打包 解压到某文件夹 -》tar -参数 压缩包 -C 路径 (命令)
tar大全:http://www.jb51.net/LINUXjishu/43356.html
mount 设备节点 挂载点 (命令)
ssh root@ip:文件所在路径 存放路径 (ssh到远程服务器下载文件到本地)


4、vim
git clone 代码地址  (在github下载代码)
编辑模式
插入模式
最后一行模式(输入命令)

vi常用技巧
撤销:u
恢复:ctrl+r

复制1行:yy
复制n行:nyy
复制1个单词:yw
复制n个单词:nyw

剪切1行:dd
剪切n行:ndd
剪切1个单词:dw
剪切n个单词:ndw

黏贴:p
定位到文件的末尾:G
定位到文件的开头:gg

定位到第几行::800(定位到800行)
设在行号:set nu (set nonu)

o (下一行插入)
0跳到行首  $跳到行尾



/+要找的内容,n向下切换页,N向上切换页
:e filename 打开文件进行编辑
:n1,n2 co n3 将n1到n2之间的内容cp到n3下
:n1,n2 d  将n1到n2之间的内容删除
:s/p1/p2/g 将当前行的p1全部换成p2
:g/p1/s//p2/g 将所有p1换成p2
:n1,n2 s/p1/p2/g 将n1到n2之间 p1换成p2

vim  ctags插件(可以找到函数定义)  apt-get install ctags

5、gcc
预处理器(展开头文件及宏定义)->gcc编译器:编译c
g++编译器:编译c++
二进制工具:as(汇编器)、ld(链接器)

gcc编译过程
    -------------------------------------------------------------------------------->
   |                                                                                            |
hello.c--------->hello.i------>汇编代码-------->目标代码-------->可执行文件
         a预处理          b编译             c汇编器             d链接器
a、gcc -E hello.c -o hello.i
b、gcc -S hello.i -o hello.s
c、gcc -C hello.s -o hello.o
d、gcc -o hello hello.o

编译常用参数:
-o 指定了生成文件的名称
-Wall 生成所有警告信息(最后加)
-w 不生成警告信息(最后加)
-static 不支持动态共享库,把函数库内容静态链接到可执行程序中
-shared 生成支持动态库的可执行程序
-I  指定额外的头文件的搜索路径
-L 指定额外的库函数的搜索路径

动态库、静态库
静态链接
优点:依赖小、兼容性好。缺点:程序大、库更新程序需重新编译

动态链接
优点:程序小,进程可共享
缺点:依赖动态库,不能独立运行

制作静态链接库(在linux中后缀为.a,以lib开头,如libmylib.a)
先制作源文件(include/mylib.h、lib/mylib.c、test.c)

gcc -c mylib.c -o mylib.o 编译目标文件
ar rc libmylib.a mylib.o 制作静态库

使用静态库 1:
gcc -o test test.c -I include(头文件路径) -L lib(库文件路径) -lmylib

制作动态链接库
gcc -shared mylib.c -o libmylib.so

使用动态库1
gcc -o test test.c -I include(头文件路径) -L lib(库文件路径) -lmylib

编译通过,运行时出错,编译时找到了库函数,但链接时找不到库,执行以下操作,把当前目录加入搜索路径
export LD_LIBRARY_PATH=.so文件的路径LD_LIBRARY_PATH

6、交叉编译器
搭建即安装、配置交叉编译工具链。
宿主机在该环境下编译出嵌入式Linux系统所需的操作系统应用程序等,然后再上传到目标机上。

快速搭建交叉编译环境
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install build-essential
sudo apt-get install gcc
sudo apt-get install gcc-arm-linux-gnueabihf-
sudo apt-get install file

apt-get 基本命令
sudo apt-get update 更新源
sudo apt-get upgrade 更新已安装的包
sudo apt-cache search package 搜索包
sudo apt-cache show package 获取包的相关信息,如说明、大小、版本等
sudo apt-get install package 安装包
sudo apt-get install package - - reinstall 重新安装包
sudo apt-get remove package 删除包
sudo apt-get source package 下载该包的源代码
diff 命令常用来比较文件、目录,也可以用来制作补丁文件。
diff -urNwB 1.c 2.c >3.diff
patch命令被用来打补 ,就是根据补丁文件中指明了要修改的文件的路径
patch <3.diff

7、git
代码工具、分散式、版本控制系统
git config --global user.name “git用户名”
git config --global user.email (git登陆邮箱)

git init 初始化命令
git status 查看git仓库状态
git add 文件 将文件放进暂存区(文件修改后要重新add、因为执行的还是原来暂存的)
显示 modified(被修改)

git remote add origin https://github.com/code-marquis/git-test.git
git push -u origin master(将本地上的 origin 提交到github、master总分支)
git branch<分支名>  创建分支
git checkout<分支名>  切换分支
git merge  <分支名>  合并分支
git log 查看提交日志
git tag 标签

在子分支下添加、修改、提交保存创建的文件。在切换的主分支是看不到在子分支创建的文件
但分支合并有文件冲突后 能在文件标示冲突信息,需要修改冲突文件
在冲突还没有解决的话,不能切换到其它分支

在远程仓库创建分支
git push 到远程仓库 只是提交最后一次的操作
git push origin test-project0.1(分支) 将子分子放到远程仓库

详细方法:http://www.liaoxuefeng.com/wiki/ ... 8c67b8067c8c017b000


已有 1 人评分威望 金钱 贡献 收起 理由
蓝天-彭 + 10 + 10 + 10

总评分: 威望 + 10  金钱 + 10  贡献 + 10   查看全部评分

回复

使用道具 举报

3

主题

0

好友

490

积分

中级会员

Rank: 3Rank: 3

贡献
100
金钱
177
威望
100
买家信用
卖家信用
积分
490
发表于 2014-9-24 19:28:47 |显示全部楼层
本帖最后由 Marquis 于 2014-9-24 19:48 编辑

8、Makefile
规则:“被修改”
(目标文件:依赖文件)
a.o : a.c
       gcc -c -o a.o a.c (当a.o的时间比a.c的要老。判断a.c已经被修改)

简化
test : test.o a.o b.o
        gcc -o $@ $^

%.o : %.c   
        gcc -c -o $@ $<  ($@ $^ $< 自动变量)

gcc -o test test.c a.c
test.c 预处理
         编译
         汇编   -》1.0
a.c     -》2.0

链接   test.c  a.c
gcc -c -o test.o test.c
gcc -c -o a.o a.c  (修改a.c 只需要执行这一句及链接)
gcc -o test test.o a.o

(1)初始
gcc -c -o 1.o 1.c
gcc -c -o 2.o 2.c

gcc -c -o 1000.o 1000.c
gcc -o test 1.o 2.o …  1500.o
(2)修改1500.c
gcc -c -o 1500.o 1500.c
gcc -o test 1.o 2.o …  1500.o

有的编译出.o文件需要加上头文件才能更新此文件
%.o : %.c %.h
        gcc -c -o $@ $<

简化(带.h文件)
test : test1.o a.o b.o c.o
        gcc -o $@ $^

%.o : %.c
        gcc -c -Wp,-MD,$@.d -o $@ $<

//完整1
objs = test1.o a.o b.o c.o

test : $(objs)
        gcc -o $@ $^

%.o : %.c
        gcc -c -Wp,-MD,$@.d -o $@ $<

deps := $(foreach v, $(objs), $(v).d)
     (循环查找objs.d文件)
deps := $(wildcard $(deps))
     (判断文件是否对应)

ifneq ($(deps),)
    (判断存在)
include $(deps)
   (将.o.d文件的内容包含进来)
endif

clean:
   (清除文件)
        rm $(objs) $(deps) *.d

对于子目录,最好是进入子目录在make

make -f makefile重命名
.phony 假象目标,目标后面的命令永远成立

9、系统启动与Bootloader
soc(ARM)-》bootloader-》kernel-》挂载rootfs-》应用程序
驱动-》DDR控制器、声卡控制器、网卡控制器
BROM 32k 一小段程序
BROM-》SDC-》NAND-》SDC2-》SPI ANAD
                                -》       -》            -》
                   -》bootloader(SPL-》uboot)-》kernel-》rootfs

SPL程序流程如下:
初始化ARM处理器
初始化串口控制台
配置时钟和最基础的分频
初始化SDRAM
配置引脚多路复用功能
启动设备初始化(即上面选择的启动设备)
加载完整的uboot程序并转交控制权

u-boot编译
make mrproper (先清除依赖)
make cubietruck_config 配置 -》makefile  Cubietruck_config ::
         目标                              @$(mkconfig)
                                              MKCONFIG    :=  $(CURDIR)/mkconfig
                                                                         当前目录(定义好)
                                               mkconfig  -A  Cubietruck
      CONFIG_NAME=sun7i                             $#  8
      BOARD_NAME=Cubietruck                      $1  Ative
      arch=arm                                              $2  arm
      cpu=armv7
      board=sunxi       soc=sunxi      tmp=sunxi     options=CUBIE.......       

uboot烧写进TF卡
先去除挂载的tf卡 umount /dev/..
将 SD 卡插入读卡器,插进 PC
$umount /media/XXX
// 卸载分区挂载,可能不止一个分区,XXX 换成正确
的路径,也有可以 PC 不自动挂载 SD 卡

$sudo fdisk -l 看 SD 卡在哪个设备节点

$card=/dev/sdc
(TF卡路径)
dd if=要烧写的文件 of=/dev/sdc(TF卡路径) bs=1M count=1
$fdisk /dev/sdc(TF卡路径) 分区
格式化分区:
$sudo mkfs.vfat ${card}1
$sudo mkfs.ext4 ${card}2

#需要稍等片刻
然后写入 bootloader :
$cd u-boot-sunxi/
$sudo dd if=u-boot-sunxi-with-spl.bin of=$card bs=1M seek=8
bs:块大小 seek:指定跳过8kb
拔出读卡器,将卡插进 CT,插电启动
sync (在拔出u盘前,将内存数据写进TF卡)

10、内核
拷贝kernel_deconfig到linux源代码根目录下。(cp kernel_deconfig sun-linux/.config     
或代码根目录:make sun7i_defconfig,获取到sun7i对应的.config)

配置参数 .config
make mrproper(删除所有的编译生成文件, 还有内核配置文件, 再加上各种备份文件)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 uImage modules(驱动模块)

编译出的内核文件:arch/arm/boot/uImage和准备好的 uEvn.txt boot.scr script.bin 复制到sd卡的第一分区

文件系统启动阶段
断电,拔出 SD,插回 PC,正常会自动挂载,将编译内核生成的 modules 安装第二分区(将XXX 改为正确路径)
$cd kernel-source
$sudo make INSTALL_MOD_PATH=/media/XXX ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_install
$cd ..
$sudo tar -C /media/XXX --strip-components=1 -zxvf linaro-quantal-alip-20130422-342.tar.gz
$sync

uboot------------》kernel-》
        配置参数

11、驱动模块编译
make sunxi7_defconfig
在linux源码根目录生成.config配置文件

make menuconfig ARCH=arm
-M-修改成驱动模块,编译出来,保存

保存之后,让他准备一下:
make prepare
make scripts

4.现在我们进入要编译的驱动的源码目录,比如sun4i-gpio.c在 drivers/misc 目录下:
cd /home/lany/workspace/linux-sunxi/drivers/misc/
make -C /home/lany/workspace/linux-sunxi/ M=`pwd` modules

modinfo检查模块版本

驱动添加
在驱动所在目录 makefile 后面按相同格式添加,并把驱动.c、.h等源文件加进这个目录下
驱动目录下的Kconfig (bool Y N)会被menuconfig调用,在驱动添加的时候,也要在kconfig文件添加相关配置,格式相同

回复

使用道具 举报

3

主题

0

好友

490

积分

中级会员

Rank: 3Rank: 3

贡献
100
金钱
177
威望
100
买家信用
卖家信用
积分
490
发表于 2014-9-24 19:29:08 |显示全部楼层
本帖最后由 Marquis 于 2014-9-24 20:01 编辑

12、根文件系统
更换根文件系统,需要更换对应uImage
kernel-----------》rootfs
/sbin/init(第一个用户态程序)

busybox(简单文件系统)
制作:
sudo wget http://busybox.net/downloads/busybox-1.21.1.tar.bz2
sudo tar jxvf busybox-1.21.1.tar.bz2
cd busybox-1.21.1
sudo make menuconfig ARCH=arm
set busybox settings → build option → Cross Compiler prefix to “arm-linux-gnueabihf-”
sudo make
sudo make install
sudo mount /dev/sdb2 /mnt
sudo cp -Rv _install/* /mnt
sudo cp -Rv examples/bootfloppy/etc /mnt
cd /mnt
sudo mkdir dev proc sys var home tmp mnt run boot boot2 dev/pts
sudo rm etc/fstab
sudo vi etc/fstab   
加入:
proc /proc proc nosuid,noexec,nodev 0 0
sysfs /sys sysfs nosuid,noexec,nodev 0 0
devpts /dev/pts devpts gid=4,mode=620 0 0
tmpfs /tmp tmpfs defaults 0 0
devtmpfs /dev devtmpfs mode=0755,nosuid 0 0
/dev/mmcblk0p1 /boot2 vfat defaults 0 2
/dev/mmcblk0p2 / ext4 defaults,noatime 0 1
保存退出

sudo chmod 777 etc/fstab
在busybox添加源码,/archivals

13、添加内核模块
#include<linux/init.h>
#include<linux/module.h> (必要头文件)

驱动模块
insmod xx.ko (加载到内存)
rmmod xxx.ko(自动找到模块并加载)modprobe  xxx

查看 lsmod
(查看模块信息)modinfo xxx.ko

同内核版本,非同一套内核可能编译出来的.ko无法使用。

在加载模块报错:“no symbol version for module_layout”
解决办法:在linux源码根目录添加文件Module.symvers

14、内核剪裁
硬件-》iRom(有些ARM没有)-》uboot(调用thekernel引导内核)-》kernel-》rootfs-》应用
linux内核从存储外设(硬盘/SD/Nand)-》拷贝到内存运行
初始化系统时钟   初始化串口(UART,调试程序) 点亮lcd    初始化lcd   初始化外设
初始化内存    重定位kernel    the kernel

看门狗:定时检测系统是否异常

uboot 启动流程(2440)(start.s)
(关闭看门狗、有些芯片已经默认关闭)
初始化系统时钟、初始化内存、初始化nand、重定位kernel、初始化UART、thekernel(0,362,0x30000100)
(2440)物理内存起始地址:0x300000000
362:机器ID, 0x30000100:启动参数首地址、指定kernel取参数的内存地址

commmandline_tag(“noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0”)
设置rootfs地址 ,应用程序的第一个应用init ,console 控制台的打印方式

内核启动流程(head.s)
head.s
ENTRY(入口)、_lookup_processor_type(判断linux是否支持此cpu)
                       _lookup_machine_type(判断linux是否支持该单板)
                       _creat_page_tables(创建页表)
                       _enable mmu  (使能mmu)
                       _switch_data (数据处理)
                       start_kernel  (初始化工作)
                       set_arch(取出启动地址)
                       rest_init(启动应用程序init)

物理地址---------------------虚拟地址  
              映射到(MMU)
       物理地址          虚拟地址
0x0~ox100000         0x100000~0x200000
通过.的物理地址和.的虚拟地址相减获得偏移值,计算_arch_info_begin(虚拟地址和.的偏移值相加获得它的实际地址)和_arch_info_end(虚拟地址和.的偏移值相加获得它的实际地址)的实际地址

_arch_info_begin
*(.arch.info.init)   (结构体)
_arch_info_end

.c参数 1    2    3
.s        r0  r1  r2

thekernel-->vmlinux->zImage、 uImage -》自解压代码+vmlinux
判断linux是否支持此cpu   判断linux是否支持该单板  创建交表  使能mmu  处理一些数据  start_kernel(初始化工作)   挂载文件系统/启动应用  

block :块设备     drivers:驱动目录   fs:文件系统目录     init:初始化相关的函数
crypto:加密文件  Documentation:帮助文档   include:头文件  ipc:通信
kernel:本身管理代码   lib:库文件   mm:存储管理   net:网络设备、协议代码
scripts:脚本文件   security:密码相关   sound:声卡相关程序   

内核编译方法
Kconfig-》
make menuconfig (y编译进内核、m编译为模块、n不编译)
make config(命令行)
make xconfig(需要依赖qt库)
make sun7i_defconfig (默认.defconfig配置文件/arch/arm/config/)

-》.config -》Makefile

make   生成zImage
make clean  清理

Kconfig:
menu(在menuconfig生成目录)
depends on(依赖关系,只有选中才会出现menu)
config  XXX(在.config生成CONFIG_XXX)
bool “XXX” 只能配置成Y、N
tristate “XXX” 可以配置为Y、N、M

Makefile:
obj-$(CONFIG_hello_world)     += hello.o

CONFIG_hello_world == config  hello_world(Kconfig)

在上层driver/Kconfig 加上source "drivers/hello/Kconfig"  driver/makefile加上
obj-y += hello/

14、开发板测试固件
测试固件:1、体积小
                2、系统启动快
                3、测试硬件功能
测试程序-脚本(查找设备相关文件是否存在) infow:绘制命令 logw:打印信息

开机启动----ir
             ----绘制硬件测试信息(硬件文件检测)
             ----测试网卡、声卡
rcS  加上ifconfig > 文件(将打印信息存入文件里)

15、GPIO驱动
硬件----》kernel----》lib---》app
                       swi      api
会在/dev/ 下创建设备节点(操作硬件-open()--打开该硬件设备节点)

MODULE_AUTHOR()驱动程序作者
MODULE_DESCRIPTION()驱动说明

dmesg(查看驱动日志)
lodcat(查看应用日志)

驱动框架
驱动入口、出口函数----定义出、设置file_operations-----定义要用到的函数(open()、write())-----注册-----创建设备节点(mknod XX(设备节点名) c(c字符设备,b块设备)主设备号 0),

man 函数名(查看头文件)
register_chrdev(主设备号,驱动名,&file_operations名)(注册)
unregister_chrdev(主设备号,驱动名)
open(设备节点路径,权限(O_WDWR))write(fd,a,1); fd:句柄 , a:要写的数据(传到驱动write的buf指针保存),1:长度

copy_from_user():获取从应用传来的值
//自动创建设备节点

        hello_dev_class = class_create(THIS_MODULE,"hello");
        device_create(hello_dev_class,NULL,MKDEV(HELLO_MAJOR,0),NULL,"hello");//dev/hello

自动创建设备节点
ioctl:应用程序控制硬件驱动
IS_ERR() (调试)

PULL:控制上拉电阻、下拉电阻
DIR:方向寄存器(配置输入还是输出)
数据寄存器
CONFIG寄存器

PH21----LED1            PH20-----LED2
PH_CFG2
PH21_select  001        PH20_select  001
映射地址
PH_DATA :用到的led在芯片引用的管脚,如PH21 则在21为置1则置高。

16、设备驱动模型---平台总线
驱动程序由两部分组成:硬件相关内容和纯软件(对硬件进行操作)的通用部分

Linux内核分离思想
就是将硬件相关内容和纯软件(对硬件进行操作)的通用部分分离开,让驱动开发者关注硬件相关的部分,而通用的操作流程(open、read......)基本不需要维护和修改

平台总线,platform_bus,虚拟的,而非硬件真实存在的总线

平台总线维护着两个链表:dev,drv

dev链表里存放硬件相关的信息

drv链表里存放对硬件操作的流程(纯软件部分)
平台总线里面有一个match函数,它进行dev和drv链表的两两匹配工作,通过dev的name和drv的name匹配,如果匹配成功会调用drv里面的probe函数,probe函数的实现由驱动开发作者实现,而具体的硬件信息在匹配时候已经得到

匹配过程:
当向总线注册dev信息(添加节点到dev链表)同时会从drv链表里取出每一个drv节点,跟自己的dev的name进行匹配,如果匹配成功会调用drv的probe

当向总线注册drv信息(添加节点到drv链表)同时会从dev链表里取出每一个dev节点,跟自己的drv的name进行匹配,如果匹配成功会调用drv的probe

dev和drv链表对应的结构?
grep -rhnw platform_bus_type ./*
(搜索平台总线结构体)
dev对应结构:struct platform_device
里面至少有个name信息
里面有硬件相关信息:struct   reource
drv对应结构:struct platform_driver
有probe函数remove(卸载函数)driver(里面有个name,跟platform_device的name匹配)

如何注册dev和drv?
platform_device_register 向内核注册一个platform_device
platform_driver_register 向内核注册一个platform_driver

Probe如何获得硬件资源?
获取的是硬件对应dev的结构体
probe函数的行参数pdev指针就是指向开始注册的platform_device结构
这个结构体本身在初始化时候就包含硬件相关信息(struct   reource可用这个存放自己特定的硬件信息),后续驱动开发者根据需求实现probe的编写,例如:注册一个字符设备

错误: ‘led_probe’未声明(不在函数内)
解决办法:将 static int led_probe(struct platform_device *pdev)写在static struct platform_driver led_driver={}上面

struct   reource  硬件资源的信息

.start:起始位(寄存器物理地址)
.end:终止位
.flags:标志,资源类型(内存:IORESOURCE_MEN,中断:IORESOURCE_IRQ,DMA通道:IORESOURCE_DMA)

res=platfrom_get_resource(dev,IORESOURCE_MEN,0);

dev:指向注册好的 platfrom_device结构
资源信息 IORESOURCE_MEN
索引0(若很多资源信息,才判断索引是哪个资源信息)
回复

使用道具 举报

3

主题

0

好友

490

积分

中级会员

Rank: 3Rank: 3

贡献
100
金钱
177
威望
100
买家信用
卖家信用
积分
490
发表于 2014-9-24 19:29:44 |显示全部楼层
本帖最后由 Marquis 于 2014-9-24 20:12 编辑

17、I2C
i2c_sunxi_xfer主要7位地址 第一位地址为设备地址
SCL:时钟线,时钟是由CPU(主端)提供
SDA:数据线,可以是输入或输出

I2C总线可以连接很多设备,cpu如何进行区分?
设备地址:唯一性,一般由芯片厂家定义。由芯片手册和硬件原理图共同决定

START信号
STOP信号
ACK信号,反馈信号,应答信号为低电平时,规定为有效应答位

芯片手册看时序
读写操作要根据芯片手册来进行

数据操作流程一定要看芯片手册细节:
以AT24C04为例子:
随机写一个数据到某一个地址:
1. CPU发射起始地址
2.CPU发射设备地址+写位(0)
3.CPU接收到ACK
4.CPU发送要写的芯片上的地址
5.CPU接收到ACK
6.CPU发送数据
7.CPU接收到ACK
8.CPU发送STOP信号

随机读:
1. CPU发射起始地址
9.CPU发射设备地址+写位(0)
10.CPU接收到ACK
11.CPU发送要写的芯片上的地址
12.CPU接收到ACK
13.CPU发送起始信号
14.CPU发射设备地址+读写位(1)
15.NO ACK
16.CPU发送STOP信号

时钟线和数据线如何搭配?
数据线上的数据改变在SCL为低电平时进行
从数据线上取数在SCL为高电平时进行

linux I2C子系统架构
I2C驱动架构
很多CPU内部已经集成了I2C控制器,也表示I2C的时序可由控制器发起,总线驱动的人要操作控制器的寄存器,不需要操作相应的时序

linux内核驱动框架
linux总线驱动:
它管理的对象是I2C控制器,这个驱动程序只是负责数据的传输,而不关心数据的具体含义,一般总线驱动都由厂家来提供,如果cpu中集成了I2C控制器并且linux内核支持这个CPU,总线驱动方面内核已经做好了

linux设备驱动:
它管理的对象I2C从设备,这个驱动程序关注的具体数据含义,怎么传输丢给总线驱动去做,
常用的一些也都包含内核中。

驱动框架:
app:open read write …..
********************
I2C设备驱动
at2404_open,at2404_read ,at2404_write
********************
内核提供统一的访问I2C总线的接口
i2c_transfer //老接口
SMBUS //新的接口,内核鼓励使用,兼容老的接口
********************
i2c 总线驱动   具体关注如何传输数据
********************
sunxi i2c 控制器

I2C设备驱动的实现
make menuconfig 选上总线驱动

i2c设备驱动模型:
设备-总线-驱动
内核为其定义一个虚拟的总线i2c_bus_type
这个总线上有2个链表:dev链表和drv链表
dev链表:存放的是i2c_client结构,描述硬件相关信息(最主要是设备地址)
drv链表:存放的是i2c_driver结构,描述软件相关信息(对i2c设备的操作)

每当i2c_client或者i2c_client.name跟i2c_driver.id_table.name两两比较,如果比较成功,调用i2c_driver里面probe函数,将i2c_client的地址传递给probe函数,peobe函数具体做什么,由需求来决定,比如:注册一个字符设备,这些内容跟platform总线思路一样

i2c_driver如何去使用
1.分配初始化i2c_driver
  .probe
  .remove
  .id_table//终点是其中的name字段,一定要和i2c_client要一样
  最终才能会匹配成功,调用probe函数
2.注册i2c_driver
3. 调用  i2c_add_driver进行注册

画图分析。
进内核看别人怎么用?(./drivers/media/video/sun4i_csi/device/ov7670.c,哪个name重要)

i2c_client如何去使用?
参看内核源码Documentation\i2c\instantiating-devices,关注方法1,2
1 总线号声明i2c设备
static struct i2c_board_info __initdata h4_i2c_board_info[] = {
        {
                I2C_BOARD_INFO("isp1301_omap", 0x2d),
                .irq            = OMAP_GPIO_IRQ(125),
        },

        {       /* EEPROM on mainboard */
                I2C_BOARD_INFO("24c01", 0x52),
                .platform_data  = &m24c01,
        },

        {       /* EEPROM on cpu card */
                I2C_BOARD_INFO("24c01", 0x57),
                .platform_data  = &m24c01,
        },

};

static void __init omap_h4_init(void)
{
        (...)
        i2c_register_board_info(1, h4_i2c_board_info,
                        ARRAY_SIZE(h4_i2c_board_info));
        (...)
}

如何分配初始化注册i2c_client:
方法1:
  1.分配初始化i2c_board_info结构
    struct i2c_board_info {
    char                type[I2C_NAME_SIZE]; //最终会赋值给i2c_client.name
    unsigned short        flags; //对设备的操作方式:读/写
    unsigned short        addr; //设备地址
    void                *platform_data;//传递硬件相关的私有数据信息
  }
};

  初始化:
  关键是type,addr如果有其他硬件信息,给platform_data

2.注册分配初始化好的i2c_board_info
o(int busnum,struct i2c_board_info const *info, unsigned len)
        busnum:这个实参从硬件原理图可知,当前设备挂接到哪个总线上
        info:指向分配初始化好的i2c_board_info
        len:有多少项
       
设备驱动 M  注册添加硬件资源
总线驱动 *  遍历寻找资源

3 提问:注册完这些I2C设备硬件信息以后,何时8何地使用__i2c_board_list链表??
答: 内核初始化时候,会初始化I2C总线驱动,总线驱动由
i2c_adapter结构体维护。并通过i2c_regiser_adapter来注册,注册时,会调用i2c_scan_static_board_info函数,其实就是来扫描有哪些i2c设备。如果扫描到以后,去创建i2c_client.
这种方法不适合采用insmod来动态加载。

一般来说,使用此法规则:
关于i2c_board_info的分配,初始化,注册一般都是在平台代码中完成,
切记:内核会帮你分配初始化注册i2c_client,以后I2C设备驱动
在向内核注册i2c_driver的时候,就会进行匹配。
记号,以上代码执行的顺序要早于I2C总线驱动初始化

i2c_client:
1.包含I2C设备的硬件信息
2.包含I2C总线驱动的信息(操作I2C控制器的实现)

方法2:看那个文档。
通过i2c_new_device直接去定义分配一个i2c_client

Example (from the sfe4001 network driver):
static struct i2c_board_info sfe4001_hwmon_info = {
        I2C_BOARD_INFO("max6647", 0x4e),
};

int sfe4001_init(struct efx_nic *efx)
{
        (...)
        efx->board_info.hwmon_client =
                i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);
        (...)
}
1.strut i2c_client *client;
   分配初始化一个i2c_board_info
2.client = i2c_new_device(i2c总线驱动adapter, i2c_board_info);

总结:
创建的i2c_client中相关的字段说明
.adapter = 指向总线驱动中注册i2c_adapter
.addr = 存储了设备的设备地址
.name = 设备名称,用于跟i2c_driver.id_table.name做比较
client创建完name开始匹配 ,probe。。。

案例:利用I2C设备驱动框架实现eeprom at24c04的驱动
开始写。看内核。vim ./drivers/video/backlight/tosa_lcd.c

vim ./drivers/input/touchscreen/ft5x_ts.c
i2c_master_send

问:一个实参总线驱动如何获取:
struct i2c_adapter *i2c_adap;
获取总线驱动信息
i2c_adap = i2c_get_adapter(总线编号);
释放总线驱动信息
i2c_put_adapter(i2c_adap);

数据传输流程:
1.应用程序请求一个I2C操作
2 设备驱动程序根据请求构造i2c传输数据包
3 设备驱动调用内核提供的统一接口i2c_transfer/smbus接口,将构造好的数据包发给总线驱动(i2c_adapter.algo.master_xfer)
4 最终总线驱动根据数据包的的内容(读还是写,还有数据信息),通过I2C控制器将数据在总线上进行传输。

接口内核文档:i2c_transfer (参考)
           /smbus接口
i2c_smbus_write_byte_data()
i2c_smbus_read_word_data()
用在.write、.read
回复

使用道具 举报

3

主题

0

好友

490

积分

中级会员

Rank: 3Rank: 3

贡献
100
金钱
177
威望
100
买家信用
卖家信用
积分
490
发表于 2014-9-24 20:12:49 |显示全部楼层
18、网络编程
TCP-》面向连接                         UDP-》不面向连接
可靠                                         不可靠
(传文件、准确数据)              
客户端(主动发请求)               服务器(被动反应)

TCP客户端流程:
iSocketClient = Socket(AF_INET,SOCK_STREAM,0);
创建socket句柄  参数1 表示用ipv4

iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));       
        if (-1 == iRet)
        {
                printf("connect error!\n");
                return -1;
        }
与服务器连接
tSocketServerAddr结构体
可以开始传数据(发数据:write/send ,接受数据:read/recv)
fgets(ucSendBuf, 999, stdin) 获取用户输入的数据
send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);  第二个参数:要发的数据

TCP服务器端流程:
iSocketServer = socket(AF_INET, SOCK_STREAM, 0);
创建socket句柄 判断它的返回值

iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
绑定端口号、ip地址  
tSocketServerAddr结构体
tSocketServerAddr.sin_family      = AF_INET;  //TCP
tSocketServerAddr.sin_port        = htons(SERVER_PORT);   
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
memset(tSocketServerAddr.sin_zero, 0, 8);

开始监听
iRet = listen(iSocketServer, BACKLOG);
#define BACKLOG     10
表示可以有10个客户端跟服务器连接

iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
等待客户端来连接 (没有客户端连接,此处会阻塞)
tSocketClientAddr存放客户端信息

iRecvLen = recv(iSocketClient, ucRecvBuf, 999, 0);
接收数据 ucRecvBuf 存放接收到的数据

UDP服务端流程:
创建socket句柄
iSocketServer = socket(AF_INET, SOCK_DGRAM, 0);
绑定服务器的ip和端口:
iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
接收数据:
iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
第二个参数是存放接收到的数据,第三个参数是接收的字节数
UDP客户端流程:
创建socket句柄
iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);
收发数据:
iSendLen = sendto(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0, (const struct sockaddr *)&tSocketServerAddr, iAddrLen);
参数五告诉客户端要发的服务器
回复

使用道具 举报

21

主题

2

好友

3264

积分

版主

Rank: 7Rank: 7Rank: 7

贡献
613
金钱
1279
威望
613
买家信用
卖家信用
积分
3264
发表于 2014-9-24 20:14:50 |显示全部楼层
{:soso_e179:}{:soso_e179:}
回复

使用道具 举报

8

主题

0

好友

4820

积分

论坛元老

Rank: 8Rank: 8

贡献
914
金钱
1821
威望
914
买家信用
卖家信用
积分
4820
发表于 2014-9-24 21:49:41 |显示全部楼层
很不错、、
回复

使用道具 举报

18

主题

10

好友

6796

积分

论坛元老

Rank: 8Rank: 8

贡献
1279
金钱
2576
威望
1279
买家信用
卖家信用
积分
6796
发表于 2014-9-25 09:25:27 |显示全部楼层
{:soso_e189:}
喵星人已回喵星球。
回复

使用道具 举报

113

主题

1

好友

1万

积分

版主

Rank: 7Rank: 7Rank: 7

贡献
2467
金钱
5721
威望
2467
买家信用
卖家信用
积分
13584
发表于 2014-9-25 12:45:22 |显示全部楼层
Marquis 发表于 2014-9-24 20:12
18、网络编程
TCP-》面向连接                         UDP-》不面向连接
可靠                            ...

{:soso_e179:}{:soso_e179:}{:soso_e179:}
回复

使用道具 举报

3

主题

0

好友

3104

积分

论坛元老

Rank: 8Rank: 8

贡献
590
金钱
1335
威望
590
买家信用
卖家信用
积分
3104
发表于 2015-7-6 17:01:10 |显示全部楼层
20天能学这么多{:soso_e179:}
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

QQ|Archiver|手机版|邮件群发|cubie.cc---深刻的嵌入式技术和应用讨论中文社区 ( 粤ICP备13051116号-1  

GMT+8, 2019-10-24 00:52 , Processed in 0.026907 second(s), 8 queries , Apc On.

Powered by Discuz! X2.5

© 2001-2012 Comsenz Inc. | Style by Coxxs

回顶部