今天总结一下IIC驱动的源码学习。IIC驱动也是Wince下的流接口驱动,所以配置文件的设置和其他流接口都一样,本人在以前已经对该问题做了介绍,这里就不重复了。
IIC驱动较为简单,主要的源文件有iic.h、iic_pdd.h、iic_mdd.h、iic_mdd.cpp和s
3c
6410_icc_lib.cpp五个文件,三个h文件都是关于相关数据结构和宏的定义,cpp文件是所有功能的实现。
下面主要从流接口的几个接口函数来分析驱动源码,包括DllEntry、IIC_Init、IIC_Deinit、IIC_Open、IIC_Close、IIC_IOControl、IIC_PowerUp和IIC_PowerDown。
一、在
Device.exe
加载
IIC
驱动时,首先执行的是
DllEntry
和
IIC_Init
函数,
DllEntry
函数没有任何实际的操作,这里跳过,直接看
IIC_Init
函数。
入口参数是关于IIC驱动在注册表中的路径。
144行是为PHW_INIT_INFO结构体在堆上分配空间,pInitContext存储在IIC_Init函数中获得的相关信息,并作为参数传递给IIC_Open函数。PHW_INIT_INFO结构体的定义在文件iic_mdd.h中,这里不再给出。
174到204行读取注册表中的相关信息,并将数据填充到pInitContext中。
208行在后面的IIC中断处理函数中使用,用来判断是否驱动卸载。
212行进行IIC硬件的相关初始化工作,后面分析。
222行返回pInitContext,将在后面作为IIC_Open函数的参数。
HW_Init函数在文件s
3c
6410_icc_lib.cpp中,下面分析一下该函数。
94
行
MapVirtualAddress
函数实现在本文件
s
3c
6410_icc_lib.cpp
中,
完成相关寄存器的虚拟地址映射。
100
行
InitializeGPIOPort
函数实现在本文件
s
3c
6410_icc_lib.cpp
中,完成
GPIO
中
GPB
的相关设置,使
IO
为
SCL
和
SDA
功能。
104
到
118
行创建了两个事件,
g_hTransferEvent
用于记录发送和接收数据的事件,
g_hTransferDone
用于记录发送结束或者接收结束的事件。
122
到
139
行首先向内核注册中断逻辑号(
IRQ_I
2C
),然后通过
InterruptInitialize
将中断号与相应的事件进行映射。当内核触发该中断时,通过查表触发相应的事件。
143
行创建相应的中断处理函数
IIC_IST
,该函数也在本文件中,下面进行分析。
677
行和
682
行用来判断
IIC
运行状态,前面初始化时已经设置成了
IIC_RUN
的状态,当退出
IIC
驱动的时候,修改状态为
IIC_FINISH
,从而退出中断处理线程。这是中断处理线程中经常使用的方法。
680
行用来等待
IIC
收发数据事件的发生,超时时间设置为无限等待。
685
到
709
行是对
IIC
四种状态的判断。
ARBITRATION_FAILED
代表
IIC
总线仲裁失败即在申请对总线的使用权时失败,
SLAVE_ADDRESS_MATCHED
代表收到的从设备地址与
ICCADD
寄存器里面的值相等,
SLAVE_ADDRESS_ZERO
代表收到的从设备地址为
0
,
ACK_NOT_RECEIVED
代表收到的最后一个
bit
不是
0
,即没有收到
ACK
信号。
711
行是用来判断当前设备处于何种状态,由于
CPU
总是作为主设备存在,所以只能是主设备接收和主设备发送两种状态。
741
到
756
行是主设备发送状态的情况,
if (g_uIIC_PT<g_uIIC_DATALEN)
判断数据是否发送完毕,没有则继续从
Buffer
中读取一个字节到
IICDS
寄存器中,如果发送完了,就置结束标志
bDone
,同时设置
IICSTAT
寄存器发送
STOP
信号,
g_pIICReg->IICCON &= ~(1<<4)
用来清除中断标志,没发送一个字节都需要清除中断标志。
761
到
765
行用来在接收或者发送结束的情况下,设置结束事件,以保证在程序的其他地方检测到结束事件后进行相应的处理。
二、上面操作完成之后,
IIC
驱动就已经成功加载了,当用户调用
CreateFile
函数是调用
IIC_Open
函数,下面分析该函数,只列出比较重要的内容。
PHW_INIT_INFO是初始化函数IIC_Init返回的信息结构体,而PHW_OPEN_INFO是将该函数中收集的相关信息结构体,用于返回给IIC_IOControl的参数。
pInitContext->OpenCnt用来记录IIC打开的次数。
372到378行用于初次打开IIC设备时的处理,HW_OpenFirst函数主要完成从设备地址设置和使能中断。如下:
// slave address setting
pInitContext-PDDCommonVal.SlaveAddress = DEFAULT_SLAVE_ADDRESS;
pInitContext->PDDCommonVal.InterruptEnable = DEFAULT_INTERRUPT_ENABLE;
pInitContext-PDDCommonVal.SlaveAddress = DEFAULT_SLAVE_ADDRESS;
pInitContext->PDDCommonVal.InterruptEnable = DEFAULT_INTERRUPT_ENABLE;
HW_PowerUp函数用来打开IIC设备Power,如下:
g_pSYSCONReg->PCLK_GATE |= IIC_POWER_ON;
381行完成打开IIC设备的大部分操作,HW_Open函数后面介绍。
387行用来增加IIC设备的开启数量。
HW_Open函数的绝大部分操作如下:
401到404行主要完成时钟、主从收发模式、过滤使能以及延时的设置。时钟要根据从设备的情况而设置,否则收发会出现问题,此处为了3000Hz。模式默认选择为主设备发送模式。
CalculateClockSet函数用来设置时钟分频以达到想要的时钟频率。S
3C
6410有两种模式,通过IICCON寄存器进行设置。
408
行的
DirtyBit
用来标记寄存器设置是否发生变化,每次设置寄存器时都需要判断该标志。
410
行的
HW_SetRegister
函数用来设置
IIC
的寄存器,包括
IICADD
、
IICSTAT
、
IICLC
、
IICCON
四种寄存器,如下:
if(g_OwnerContext != pOpenContext || pOpenContext->DirtyBit == TRUE)
{
DEBUGMSG (ZONE_FUNCTION,
(TEXT("+HW_SetRegister(0x%X)\r\n"),
pOpenContext));
g_pIICReg->IICADD = pOpenContext->pInitContext->PDDCommonVal.SlaveAddress;
g_pIICReg->IICSTAT = (g_pIICReg->IICSTAT & ~(0x3<<6)) | (1<<4) | (pOpenContext->PDDContextVal.ModeSel<<6);
g_pIICReg->IICLC = (pOpenContext->PDDContextVal.FilterEnable<<2) | (pOpenContext->PDDContextVal.Delay);
g_pIICReg->IICCON = (pOpenContext->PDDContextVal.ClockSel << 6) | (pInitContext->PDDCommonVal.InterruptEnable << 5) |
(pOpenContext->PDDContextVal.ClockDiv & 0xf);
g_OwnerContext = pOpenContext;
pOpenContext->DirtyBit = FALSE;
}
{
DEBUGMSG (ZONE_FUNCTION,
(TEXT("+HW_SetRegister(0x%X)\r\n"),
pOpenContext));
g_pIICReg->IICADD = pOpenContext->pInitContext->PDDCommonVal.SlaveAddress;
g_pIICReg->IICSTAT = (g_pIICReg->IICSTAT & ~(0x3<<6)) | (1<<4) | (pOpenContext->PDDContextVal.ModeSel<<6);
g_pIICReg->IICLC = (pOpenContext->PDDContextVal.FilterEnable<<2) | (pOpenContext->PDDContextVal.Delay);
g_pIICReg->IICCON = (pOpenContext->PDDContextVal.ClockSel << 6) | (pInitContext->PDDCommonVal.InterruptEnable << 5) |
(pOpenContext->PDDContextVal.ClockDiv & 0xf);
g_OwnerContext = pOpenContext;
pOpenContext->DirtyBit = FALSE;
}
三、最后在打开IIC设备之后,便是通过IIC_IOControl接口对IIC设备进行各种操作。IOControl控制码主要包括一下几种:
IOCTL_POWER_CAPABILITIES
、
IOCTL_IIC_WRITE
、
IOCTL_IIC_READ
、
IOCTL_IIC_SET_CLOCK
、
IOCTL_IIC_GET_CLOCK
、
IOCTL_IIC_SET_MODE
、
IOCTL_IIC_GET_MODE
、
IOCTL_IIC_SET_FILTER
、
IOCTL_IIC_GET_FILTER
、
IOCTL_IIC_SET_DELAY
、
IOCTL_IIC_GET_DELAY
等。
在这些控制码中,比较常用的是读写请求
IOCTL_IIC_WRITE
与
IOCTL_IIC_READ
、
时钟设置
IOCTL_IIC_GET_CLOCK
、
延时设置
IOCTL_IIC_SET_DELAY
。
时钟设置的请求,实际调用的函数依然上面提到的CalculateClockSet函数。
延时设置调用pOpenContext->PDDContextVal.Delay = (IIC_DELAY)*(UINT32*)pBufIn;
写请求主要调用
HW_Write
函数,大部分情况用来设置从设备的初始化等,位于
s
3c
6410_iic_lib.cpp
文件中,如下:
578
行根据写请求之前的各项设置重新设置相关寄存器。
580
行重置
g_hTransferDone
事件,在每次的读写操作之前都需要该操作。
582
行是通过
IICSTAT
寄存器的状态判断
IIC
总线是否空闲,如果有其他设备正在使用则需要等待其释放。
588
行到
598
行首先记录要发送数据的
buffer
以及长度,然后设置接收
ACK
信号和收发模式、从设备的写地址,最后发送
START
信号,此时
IICDS
上的数据变随着
CLK
发送到了从设备,并且每次发送完一个字节都会触发收发数据的中断处理线程函数中,前面已经介绍了。
600
行用于等待所有的数据都发送完成,包括
STOP
信号的发送。
读请求主要调用
HW_Read
函数,主要用来读取从设备的状态及有效数据等,位于
s
3c
6410_iic_lib.cpp
文件中,如下:
529
行到
539
行的内容基本都喝
HW_Write
函数的一样,唯一不同的是多了一个函数
HW_Write
,在读取从设备数据之前,首先需要发送从设备的写地址,其次需要告诉从设备你要读取哪些数据
(
大多数是要读取数据的地址
)
,所以此处会包含该函数。
540
行到
548
行首先设置接收数据的
buffer
和长度,然后设置接收
ACK
信号和设备模式、从设备的读地址等,最好发送
START
信号。
600
行用于等待所有的数据都接收完成。
本文转自jazka 51CTO博客,原文链接:http://blog.51cto.com/jazka/707401,如需转载请自行联系原作者