s3c2440裸机-I2c编程-3-i2c程序框架

发布者:快乐行者最新更新时间:2024-07-04 来源: elecfans 手机看文章 扫描二维码
随时随地手机看文章

1.iiC设备的功能

很显然,IIC控制器提供了传输数据的能力,至于数据有什么含义,IIC控制器并不知道,数据的含义有外部i2c从设备,我们需要阅读芯片手册,才知道IIC控制器应该发出怎样的数据。

下图是AT24cxx的操作方法:


2.I2c程序框架

显然我们的程序应该分为两层(IIC设备层,IIC控制器层),框架如下图所示:


最上层是i2c_test层,用来对i2c的功能进行测试和验证。

第2层是i2c设备层,用来对具体某一型号的从设备进行i2c读写

第3层是通用i2c控制器层,用来提供对具体某一型号的i2c主控进行管理操作

最底层是i2c控制器具体的型号层

在通用i2c控制层,我们提供一个统一的接口i2c_transfer,不关使用哪个芯片,他最终都会调用i2c_transfer,来选择某一款I2C控制器,把数据发送出去,或者从I2c设备读到数据。

对于每一次传输的数据都可以用一个i2c_msg结构体来表示。但是,读某个地址的数据时,就要用两个i2c_msg结构体来描述它,因为一个i2c_msg结构体只能描述一个传输方向(读/写),我们读取ac24ccxx某个地址上的数据时,要先写出要读取的地址,然后来读取设备地址上的数据。


i2c_test.c文件

该文件的内容如下:

void i2c_test(void) { /* 初始化: 选择I2C控制器 */ /* 提供菜单供测试 */ }

这个菜单最终会调用到at24cxx.c里面的函数。

at24cxx.c文件

在里面会使用标准的接口i2c_transfer来启动I2C传输。该文件的内容如下:


#define AT24CXX_ADDR 0x50 int at24cxx_write(unsigned int addr, unsigned char *data, int len) { i2c_msg msg; int i; int err; unsigned char buf[2]; for (i = 0; i < len; i++) { buf[0] = addr++; buf[1] = data[i]; /* 构造i2c_msg */ msg.addr = AT24CXX_ADDR; msg.lags = 0; /* write */ msg.len = 2; msg.buf = buf; msg.err = 0; msg.cnt_transferred = -1; /* 调用i2c_transfer */ err = i2c_transfer(&msg, 1); if (err) return err; } return 0; } int at24cxx_read(unsigned int addr, unsigned char *data, int len) { i2c_msg msg[2]; int err; /* 构造i2c_msg */ msg[0].addr = AT24CXX_ADDR; msg[0].lags = 0; /* write */ msg[0].len = 1; msg[0].buf = &addr; msg[0].err = 0; msg[0].cnt_transferred = -1; msg[1].addr = AT24CXX_ADDR; msg[1].lags = 1; /* read */ msg[1].len = len; msg[1].buf = data; msg[1].err = 0; msg[1].cnt_transferred = -1; /* 调用i2c_transfer */ err = i2c_transfer(&msg, 2); if (err) return err; return 0; }

View Code

i2c_controller.h文件

typedef struct i2c_msg { unsigned int addr; /* 7bits */ int flags; /* 0 - write, 1 - read */ int len; int cnt_transferred; unsigned char *buf; }i2c_msg, *p_i2c_msg; typedef struct i2c_controller { int (*int)(void); int (*master_xfer)(i2c_msg msgs, int num); char *name; }i2c_controller, *p_i2c_controller;

i2c_controller.c文件

该文件的内容如下:


#define I2C_CONTROLLER_NUM 10 /* 有一个i2c_controller数组用来存放各种不同芯片的操作结构体 */ static p_i2c_controller p_i2c_controllers[I2C_CONTROLLER_NUM]; static p_i2c_controller p_i2c_con_selected; void register_i2c_controller(p_i2c_controller *p) { int i; for (i = 0; i < I2C_CONTROLLER_NUM; i++) { if (!p_i2c_controllers[i]) { p_i2c_controllers[i] = p; return; } } } /* 根据名字来选择某款I2C控制器 */ int select_i2c_controller(char *name) { int i; for (i = 0; i < I2C_CONTROLLER_NUM; i++) { if (p_i2c_controllers[i] && !strcmp(name, p_i2c_controllers[i]->name)) { p_i2c_con_selected = p_i2c_controllers[i]; return 0; } } return -1; } /* 实现 i2c_transfer 接口函数 */ int i2c_transfer(i2c_msg msgs, int num) { return p_i2c_con_selected->master_xfer(msgs, num); } void i2c_init(void) { /* 注册下面的I2C控制器 */ s3c2440_i2c_con_add(); /* 选择某款I2C控制器 */ select_i2c_controller('s3c2440'); /* 调用它的init函数 */ p_i2c_con_selected->init(); }

View Code

有数组一定有注册函数register_i2c_controller会把下面实现的I2C控制器结构体i2c_controller放到i2c_controller数组里面。select_i2c_controller函数根据名字来选择某款I2C控制器后,以后就会使用被选择的I2C控制器来启动传输。

s3c2440_i2c_controller.c文件

中断服务函数,当发生中断时,就会调用中断服务函数,代码如下(详细内容见下一节):

void i2c_interrupt_func(int irq) { /* 每传输完一个数据将产生一个中断 */ /* 对于每次传输, 第1个中断是'已经发出了设备地址' */ }

s3c2440_i2c_con_init函数,用来初始化I2C,控制器代码如下:


void s3c2440_i2c_con_init(void) { /* 配置引脚用于I2C*/ GPECON &= ~((3<<28) | (3<<30)); GPECON |= ((2<<28) | (2<<30)); /* 设置时钟 */ /* [7] : IIC-bus acknowledge enable bit, 1-enable in rx mode * [6] : 时钟源, 0: IICCLK = fPCLK /16; 1: IICCLK = fPCLK /512 * [5] : 1-enable interrupt * [4] : 读出为1时表示中断发生了, 写入0来清除并恢复I2C操作 * [3:0] : Tx clock = IICCLK/(IICCON[3:0]+1). * Tx Clock = 100khz = 50Mhz/16/(IICCON[3:0]+1) */ IICCON = (1<<7) | (0<<6) | (1<<5) | (30<<0); /* 注册中断处理函数 */ register_irq(27, i2c_interrupt_func); }

View Code

1).IICCON = (0<<6) | (1<<5) | (30<<0); 设置IICCON控制寄存器。选择发送时钟,使能中断。设置ACK应答使能,bit[7]。

2).register_irq(27, i2c_interrupt_func):注册中断处理函数,当发生I2C中断的时候就会调用i2c_interrupt_func中断处理函数。

初始化完成后,就可以调用do_master_tx写I2C从机了,这个函数仅仅启动I2C传输,然后等待,直到数据在中断服务程序中传输完毕后再返回。函数代码如下:


int do_master_tx(p_i2c_msg msg) { p_cur_msg = msg; msg->cnt_transferred = -1; msg->err = 0; /* 设置寄存器启动传输 */ /* 1. 配置为 master tx mode */ IICCON |= (1<<7); /* TX mode, 在ACK周期释放SDA */ IICSTAT = (1<<4); /*IIC-bus data output enable/disable(1: Enable Rx/Tx)*/ /* 2. 把从设备地址写入IICDS */ IICDS = msg->addr<<1;//[slave addr [7:1], addr[0] is trans dir] /* 3. IICSTAT = 0xf0 (启动传输), slave addr数据即被发送出去,当到达第9个clk,无论是否有ack, 将导致中断产生 */ IICSTAT = 0xf0; /* 后续的传输由中断驱动 */ /* 循环等待中断处理完毕 */ while (!msg->err && msg->cnt_transferred != msg->len); if (msg->err) return -1; else return 0; }

View Code

1).IICDS = msg->addr<<1: 把从机地址(高7位,所以需要向右移一位)写入到IICDS寄存器中。

2).IICSTAT = 0xf0:设置IICSTAT寄存器,将s3c2440设为主机发送器,并发出S信号后,紧接着就发出从机地址。后续的传输工作将在中断服务程序中完成。

do_master_rx函数的实现和do_master_tx函数类似,代码如下:


动图封面

动图封面

int do_master_rx(p_i2c_msg msg) { p_cur_msg = msg; msg->cnt_transferred = -1; msg->err = 0; /* 设置寄存器启动传输 */ /* 1. 配置为 Master Rx mode */ IICCON |= (1<<7); /* RX mode, 在ACK周期回应ACK */ IICSTAT = (1<<4); /*IIC-bus data output enable/disable*/ /* 2. 把从设备地址写入IICDS */ IICDS = (msg->addr<<1)|(1<<0); /* 3. IICSTAT = 0xb0 , 从设备地址即被发送出去, 将导致中断产生 */ IICSTAT = 0xb0; /* 后续的传输由中断驱动 */ /* 循环等待中断处理完毕 */ while (!msg->err && msg->cnt_transferred != msg->len); if (msg->err) return -1; else return 0; }

View Code

1).IICDS = (msg->addr<<1)|(1<<0):把从设备地址写入IICDS,前7位是从机地址,第8位表示传输方向(0表示写操作,1表示读操作)。

s3c2440传输函数,根据标志位flags,来指明是读/写(1:读 0:写)。代码如下:

int s3c2440_master_xfer(p_i2c_msg msgs, int num) { int i; int err; for (i = 0; i < num; i++) { if (msgs[i].flags == 0)/* write */ err = do_master_tx(&msgs[i]); else err = do_master_rx(&msgs[i]); if (err) return err; } return 0; }

s3c2440_i2c_con_add函数把上面定义的s3c2440_i2c_con结构体注册到上层的i2c_controller数组中.

void s3c2440_i2c_con_add(void) { register_i2c_controller(&s3c2440_i2c_con); }

我们定义一个i2c_controller结构体s3c2440_i2c_con。下面的代码对他进行初始化。

static i2c_controller s3c2440_i2c_con = { .name = 's3c2440', .init = s3c2440_i2c_con_init, .master_xfer = s3c2440_master_xfer, };

框架总结如下:



引用地址:s3c2440裸机-I2c编程-3-i2c程序框架

上一篇:s3c2440裸机-nandflash编程-1-nandflash原理及结构简介
下一篇:s3c2440裸机-I2c编程-2-i2c控制器

推荐阅读最新更新时间:2024-11-02 12:19

S3C2440裸机------LCD_LCD控制器介绍
1.LCD控制器的功能 LCD控制器主要完成两个工作: 取数据:把framebuffer的地址告诉LCD控制器,bpp,分辨率。 发数据: 把时序告诉LCD控制器、设置引脚的极性。 2.LCD控制器框图 我们的LCD控制器中的LCDDMA会从内存中把数据取出来,然后发送给LCD,我们通过设置寄存器来控制LCD控制器发出合适的时序。 3.LCD像素数据格式 如果像素数据使用8bpp,那么会用到一个调色板的概念, 4.LCD控制器时序图
[单片机]
<font color='red'>S3C2440</font><font color='red'>裸机</font>------LCD_LCD控制器介绍
十三、S3C2440 裸机 — 初始化代码及MMU
13.1 NOR FLASH 搬运 把程序从 nor flash 上搬运到 SDRAM 中 程序存储在 nor flash 上,运行时将程序搬运到 SDRAM 中运行 nor flash 启动:nor flash 的地址从 0x0000 0000 开始,CPU 可以直接在 nor flash 上运行程序 在 nor flash 上运行程序很慢 SDRAM:地址为 0x3000 0000,程序烧录在 nor flash 上,运行时,将 nor flash 上的代码搬运至 SDRAM 中运行 nor flash 启动: 初始化寄存器 关闭看门狗 设置存储控制器 复制代码到 SDRAM 中 跳转 main
[单片机]
十三、<font color='red'>S3C2440</font> <font color='red'>裸机</font> — 初始化代码及MMU
s3c2440裸机-norflash4-编程实现
1.识别norflash 我们知道要识别norflash属性,要让norflash进入cfi模式,然后按照手册上的表格发送一系列的命令就能获取norflash属性。 1)发送命令 那么我们需要实现一个cpu向nor发命令的一个函数nor_cmd()。我们的norflash是16bit位宽的,所以访问nor是以16位为单位访问的。 #define NOR_FLASH_BASE 0 /* jz2440, nor-- cs0, base addr = 0 */ /* 比如: 55H 98 ** 本意是: 往(0 + (0x55) 1)写入0x98 */ void nor_write_word(unsigned int b
[单片机]
利用I2C总线实现ATmega88的在应用编程
引言 随着嵌入式系统技术的发展,电可擦除的Flash存储器由于具有容量大、成本低、编程方便等优点,在微控制器领域得到了广泛的应用Flash微控制器在正常运行前必须将Flash写入用户应用程序,目前对微控制器的Flash程序存储器进行编程的方法主要有出厂固化、编程器编程、在系统编程(In System Programming,ISP)和在应用编程(In Application programming,IAP)4种。 其中,出厂固化和编程器编程方法都要求微控制器在焊接前将程序写入,这显然不满足开发阶段的调试和日后升级的需要。目前比较普及的是在板可编程的ISP和IAP方法。ISP是通过微控制器的串行编程写入应用程序,需要少量的外
[单片机]
利用<font color='red'>I2C</font>总线实现ATmega88的在应用<font color='red'>编程</font>
s3c2440裸机-电阻触摸屏编程(6.触摸屏校准实现-五点校准法)
前面我们讲过触摸屏触摸屏校准原理就是让lcd能够与触摸屏坐标对应起来。 一、五点法校准实现 一、我们取A,B,C,D,E这五个点,那么这个时候我们需要把该5个点的触摸屏和LCD的坐标对应起来,这就是校准的过程。 ①在LCD显示屏上A点显示一个“十字”形状 ②用户在触摸屏上点击对应A点的“十字”形状 ③记录触摸屏的数据坐标 同理在B,C, D, E点循环该①②③过程,就能得到这五点触摸屏坐标。 二 、然后根据这5个触摸屏坐标数据确定公式。 三 、以后得到TS触点坐标,即可校准出期待的TS坐标。 下面开始函数实现: 在LCD上显示 十字 形状,定义为函数fb_disp_cross() 记录触摸屏坐标,定义函数为ts_read_ra
[单片机]
<font color='red'>s3c2440</font><font color='red'>裸机</font>-电阻触摸屏<font color='red'>编程</font>(6.触摸屏校准实现-五点校准法)
S3C2440-裸机篇-08 | 使用S3C2440操作SDRAM(配置内存控制器)
1. 前言 提起SDRAM,大家都会觉得太难了,要编程写出SDRAM的控制时序更是难上加难,对的,没错!一年前我也是这样想的,学习这一节内容的时序觉得非常难,视频看了好几遍不太懂,对于SDRAM的控制原理更是没看懂,一年后回过头来再看视频,茅塞顿开,看不懂的原因是因为:我自己把它想的太难了,其实,它很简单,总共也就5行代码,设置5个寄存器即可。 简单的原因要归功于S3C2440内部的内存控制器,它的作用就是负责向外部扩展的存储类设备提供控制信号,所以当CPU要去访问属于SDRAM时,只需要去访问属于SDRAM的映射地址即可,内存控制器会发出信号,控制时序去和SDRAM打交道,写入数据或者是读出数据。 尽管我们不用手写操作时
[单片机]
S3C2440-<font color='red'>裸机</font>篇-08 | 使用<font color='red'>S3C2440</font>操作SDRAM(配置内存控制器)
STM32F030 I2C 从模式中断编程
第一次用I2C的从模式,之前用的是主模式,用的IO模拟的,在很多设备都用上了,没什么问题。在使用I2C从模式之前,也在网上看到很多人说这个是坑。自己花了几天的时间,终于跳过了这个坑,再次总结下: 1. 最困难的地方:因为需要两个平台对接,主端用的是LINUX系统,芯片是TI335X,从端是STM32F030,我两端的程序都没写过,所以出现问题了,无法判定是哪一边的问题,这个很痛苦。 2. 例子。虽然“拿来主义”不太厚道”,但是站在巨人的肩膀上,往往是最快的。 1) STM32F030的代码:http://www.openedv.com/forum.php?mod=attachment&aid=NDczMzV8N2FkM
[单片机]
s3c2440裸机-内存控制器3-SDRAM原理-cpu是如何访问sdram的
1.SDRAM原理 black (1)SDRAM内部存储结构: (2)再看看与2440连接的SDRAM原理图: sdram引脚说明: A0-A12:地址总线 D0-D15:数据总线(位宽16,2片级联成位宽32) BA0-BA1:bank选择 nSCS:片选 nSRAS:行地址选择 nSCAS:列地址选择 nWE:写使能 SCLK:时钟 SCKE:时钟使能 (3)SDRAM的地址范围: 之前我们讲“二、不同位宽外设与CPU地址总线的连接”这一节的时候,我们留下了一个问题,SDRAM的地址范围是多少? 我们知道地址范围肯定是base_addr + size。我们根据片选接了nGCS6,base_addr=0x30
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved