N24 IIC子系统以及驱动知识点
一、IIC子系统IIC子系统实现基于IIC总线驱动的IIc设备驱动。设备与驱动分离总线驱动和设备驱动分层。#include linux/init.h #include linux/printk.h #include linux/kdev_t.h #include linux/types.h #include linux/fs.h #include linux/export.h #include asm/uaccess.h #include asm/string.h #include asm/io.h #include linux/miscdevice.h #include linux/module.h #include linux/of.h #include linux/of_irq.h #include linux/i2c.h #define DEV_NAME lm75 static struct i2c_client * pclient; static int open(struct inode * node, struct file * file) { printk(lm75 open...\n); return 0; } static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset) { int ret 0; short temp; unsigned char data[2] {0}; struct i2c_msg msg { .addr pclient-addr, .flags I2C_M_RD, .len 2, .buf data }; ret pclient-adapter-algo-master_xfer(pclient-adapter, msg, 1); if(ret 0) return ret; temp ( data[0] 8 | ((data[1] 7)) 8) / 2; ret copy_to_user(buf, temp, sizeof(temp)); printk(lm75 read...\n); return ret; } static int close(struct inode * node, struct file * file) { printk(lm75 close...\n); return 0; } static struct file_operations fops { .owner THIS_MODULE, .open open, .read read, .release close }; static struct miscdevice misc { .minor MISC_DYNAMIC_MINOR, .name DEV_NAME, .fops fops }; static int probe(struct i2c_client * client, const struct i2c_device_id * id) { int ret misc_register(misc); if(IS_ERR_VALUE(ret)) goto err_misc; pclient client; printk(lm75 probe misc_deregister slave addr 0x%02x...\n, pclient-addr); return 0; err_misc: printk(lm75 probe err ret %d\n, ret); misc_deregister(misc); return ret; } static int remove(struct i2c_client * client) { misc_deregister(misc); printk(remove lm75 misc_deregister ##############\n); return 0; } static const struct of_device_id match_table[] { [0] {.compatible ti,lm75} }; static const struct i2c_device_id lm75_id_table[] { [0] {.name ti,lm75} }; static struct i2c_driver lm75_driver { .probe probe, .remove remove, .driver { .name DEV_NAME, .of_match_table match_table }, .id_table lm75_id_table }; static int __init lm75_init(void) { int ret i2c_add_driver(lm75_driver); if(ret 0) goto err_reg; printk(lm75 i2c_add_driver ...\n); return 0; err_reg: printk(lm75 i2c_add_driver err ...\n); i2c_del_driver(lm75_driver); return ret; } static void __exit lm75_exit(void) { i2c_del_driver(lm75_driver); printk(lm75 i2c_del_driver ...\n); } module_init(lm75_init); module_exit(lm75_exit); MODULE_LICENSE(GPL);二、spi总线ADXL345——3 轴数字加速度传感器I2C / SPI 接口。1.首先spi的设备树 DTS 配置ecspi3 { fsl,spi-num-chipselects 1; cs-gpio gpio1 20 GPIO_ACTIVE_LOW; /* cantt use cs-gpios! */ pinctrl-names default; pinctrl-0 pinctrl_ecspi3; status okay; triaxis: adxl3450 { compatible adxl345; spi-max-frequency 4000000; reg 0; //片选 }; };2.ADXL345 重要寄存器配置ADXL345 SPI 读 / 写指令格式写操作发送 2 个字节第 1 字节寄存器地址最高位0第 2 字节数据读操作先发送地址再读数据第 1 字节寄存器地址最高位1→ 表示读第 2 个时钟周期回读数据也就是读 地址 | 0x80写 地址最高位 0static unsigned char adxl345_read(unsigned char reg_addr) { unsigned char data 0; int ret 0; unsigned char tx_data (reg_addr | 0x80);读指令最高位1 struct spi_message msg; struct spi_transfer transfer[2] { [0] { .tx_buf tx_data, .len 1 }, [1] { .rx_buf data, .len 1 长度 1 字节 } }; spi_message_init(msg); 初始化消息 spi_message_add_tail(transfer[0], msg); spi_message_add_tail(transfer[1], msg); ret spi_sync(adxl345_device, msg); 同步传输 if(ret 0) return ret; return data; } static int adxl345_write(unsigned char reg_addr, unsigned char data) { int ret 0; unsigned char tx_data[2] {0}; 写 2字节 struct spi_message msg; struct spi_transfer transfer 只需要一段传输 { .tx_buf tx_data, .len 2 }; tx_data[0] reg_addr; tx_data[1] data; spi_message_init(msg); spi_message_add_tail(transfer, msg); ret spi_sync(adxl345_device, msg); if(ret 0) return ret; return ret; } static void adxl345_init(void) { adxl345_write(0x2E, 0x08); INT_ENABLE 使能 DATA_READY 中断 adxl345_write(0x31, 0x0B); DATA_FORMAT ±16g13位全分辨率 adxl345_write(0x2C, 0x08); BW_RATE 输出速率 12.5Hz adxl345_write(0x2D, 0x0B); POWER_CTL 测量模式 8Hz睡眠 }3.spi配置#define DEV_NAME adxl345 static struct spi_device * adxl345_device; static int open(struct inode * inode, struct file * file) { adxl345_init(); printk(adxl345 open\n); return 0; } static ssize_t read(struct file * file, char __user * buf, size_t size, loff_t * loff) { int ret 0; short data[3]; data[0] adxl345_read(0x32) | (adxl345_read(0x33) 8); data[1] adxl345_read(0x34) | (adxl345_read(0x35) 8); data[2] adxl345_read(0x36) | (adxl345_read(0x37) 8); ret copy_to_user(buf, data, sizeof(data)); return 0; } static int probe(struct spi_device *spi) { unsigned char adxl345_id 0; int ret misc_register(misc_dev); if(ret 0) goto err_misc_register; spi-mode SPI_MODE_3; 设置模式ADXL345 硬件只支持 SPI MODE 3 ret spi_setup(spi); if(ret 0) return ret; adxl345_device spi; adxl345_id adxl345_read(0); 读取 0x00 寄存器 #ADXL345 的 DEVID 寄存器固定值 0xE5 printk(adxl345 probe spi mode %d id %#x\n, spi-mode, adxl345_id); return 0; err_misc_register: printk(adxl345 probe misc_register failed\n); return ret; } static int remove(struct spi_device *spi) { misc_deregister(misc_dev); printk(adxl345 remove\n); return 0; }三、系统移植把 Linux 内核 根文件系统 驱动从官方通用版本搬到你自己的开发板 / 硬件平台上让它能正常开机、运行、驱动外设。烧写到开发板上①系统移植的 4 大核心部分移植一共就做这 4 件事1. 交叉编译环境搭建在 PCUbuntu上安装ARM 交叉编译器作用电脑编译出能在 ARM 开发板运行的程序2. U-Boot 移植引导程序相当于电脑的 BIOS负责初始化硬件 → 加载 Linux 内核 → 启动内核移植内容修改时钟、DDR、串口、Flash、网络等3. Linux 内核移植最核心这就是你刚才看 SPI 结构体的地方内核移植主要做选择CPU 平台配置打开 / 关闭内核功能移植驱动串口、GPIO、SPI、I2C、网口、LCD、触摸屏等配置设备树DTS编译生成zImage或uImage4. 根文件系统rootfs移植提供命令、库、应用程序常用BusyBox 构建②驱动知识点Linux 存储分区结构分区名称核心作用存储位置关键特性ROM 固件芯片出厂固化的第一级启动代码CPU 内部 ROM不可修改硬件级启动入口仅负责加载 U-BootUboot第二级引导程序系统 BIOSeMMC/SD 卡的 Boot 分区初始化硬件、加载内核、设备树、根文件系统zImage压缩后的 Linux 内核镜像eMMC/SD 卡的内核分区内核启动入口包含所有驱动、进程管理等核心功能dtb设备树二进制文件Device Tree BlobeMMC/SD 卡的设备树分区描述硬件资源让内核适配不同硬件无需修改内核代码rootfs根文件系统Root FilesystemeMMC/SD 卡的根文件系统分区系统运行的基础包含命令、库、驱动、服务等所有用户空间资源user用户数据分区eMMC/SD 卡的剩余空间存放应用程序、业务数据、日志等用户自定义内容烧写模式修改 U-Boot 里bootcmd自动生成 / 自动执行的命令:setenv bootcmd tftp 0x80800000 zImage; tftp 0x83000000 pt.dtb; bootm 0x80800000 - 0x83000000 saveenv内核定时器内核定时器 内核里的 “闹钟”到了指定时间内核自动调用你写的函数只执行一次如果想循环需要重新设置基于jiffies内核心跳.expires jiffies HZ; // 超时时间1秒后记录节拍。用途延时处理周期性扫描按键ADXL345 定时读取数据硬件超时处理一般情况毫秒以下用delay毫秒以上用sleep中断上下文只能用忙等待delay中断上下文紧急任务不能拖延 不能调度、不能休眠。中断处理函数内核定时器回调tasklet工作队列除外工作队列是进程上下文高精度定时器hrtimer这是 Linux 内核中微秒 / 纳秒级高精度定时的核心方案专门解决传统timer_list基于 jiffies 精度不足毫秒级的问题是嵌入式驱动、实时任务开发的必备工具。异步同步IO同步 I/O我发命令 → 等它做完 → 再往下走异步 I/O我发命令 → 不等它做完 → 直接干别的 → 做完了通知我