小组 > 单片机 > STM32爱好者之家 > 《STM32F0308-DISCO套件》使用心得---硬件中断驱动的I2C
?
?
小组信息
名称:STM32爱好者之家
人数:6661
小组积分:72169
简介:欢迎进入STM32小组,学习讨论相关技术、交换资料、交流学习经验和设计心得。QQ群:179724650

小组公告

福利:免费申请STM32F0308-DISCO开发套件(Discovery kit with STM32F030R8 MCU) 活动马上开始,只需2分钟即可申请。申请地址:http://group.ednchina.com/GROUP_MES_14262_619_5000024284.HTM

返回本小组首页

楼主??[原创]?

《STM32F0308-DISCO套件》使用心得---硬件中断驱动的I2C


作者:s1j2h3
积分:45分
总积分:166分
:836
发送信息
加为好友
发表于2015-11-16 16:54

  STM32F030,在我看来,还是一款非常优秀的单片机,功能相对强大,性价比也较高。

  我申请该开发板是用于物联网应用,一是驱动LT8900进行无线通信,二是驱动超声波进行距离测量。以射频信号(有基站的坐标信息)作为基站发出超声波(伪随机编码)的开始时间,然后在0.1s的时间内不停采样, 然后以相关的方法解析出本机与基站之间的距离值(相关处理,温度校准等处理),最终以对至少四个基站的距离值解析出本机位置。

  本来我打算就以这个产品发一篇文章,但要讲清楚我估计可以出一本书,还得花大量时间来整理相关信息。还不一定能说清楚(我的文字功底较差)。后来想想还是有针对性地解决一个问题,以与大家相互学习,而且可以与STM32F030的器件功能紧紧贴合。

  在此我选择以I2C驱动作为突破口,描述一下stm32f030这个单片机的简单易用。现在大部分人都认为STM32中的I2C为避开别的公司在这个总线上的相关专利,而设计出的I2C模块不要用,也不易用,而宁愿用IO口模拟的方法;或者虽然用了硬件驱动的方法,不过在数据读写的过程中却用到了while()这样的语句来查询读写操作完成,白白浪费了芯片设计为此作为的努力,也不符合单片机低功耗设计的理念。

  我的这个由硬件中断驱动的I2C驱动程序,采用了分层设计与事件驱动的概念。首先,将要收发的数据及数据个数写入相应的缓存,然后开始启动I2C总线……下面开始精彩的部分

  I2C将产生相应的中断(读操作,写操作,器件地址写完成,读写结结束,其他),根据中断标识进行相应的处理,也就是说,从宏观上讲,发出启动命令后,后面怎么操作就由单片机自己完成了(首先需设定好中断处理策略)。

  对于发送过程:发完一个字节数据,产生一个中断,向发送寄存器中写入后一个字节数据,再发送,再产生中断……,直到所有的数据发送完成,模块自动产生一个停止信号。

  接收过程:接收到一个数据,产生一个中断,记录这个接收到的数据,退出中断。又接收到一个数据,再产生一个中断……,直到所以的数据接收完毕,模块自动产生一个停止信号。

  当然,在启动收到过程的配制函数中,要先设置好待接收或发送的数据个数,否则会出错;还有,在模拟运行的过程中,不可避免地会发生某些错误,中断处理的过程也必须有相应的处理,否则,死机或程序跑飞就不好玩了。

?

  下面,贴出驱动代码,这可是从全部跑顺了的工程文件中直接copy出来的,绝对可用。

注意:IO口配制相关程序在下面的源码中没有贴出,是在另一个文件中的。

   在启动读写过程之前,需要先配制两个回调函数,即i2c操作出错相应的处理程序及i2c读写操作完成后的处理函数(否则主程序不知何时操作完成,以进行其他操作)

   这个I2C驱动是针对>256字节的EEPROM进行读写操作的,所以寻址地址为16位,所以在读写操作启动时,配制的数据个数为2(对应I2C_TransferHandling函数的输入参数Number_Bytes);如果针对的是<=256字节的设备进行操作,则可以将这个参数设置为1。

   为防止I2C总线异常驱动,如在两块板子都用同一I2C数据线进行通信,而另一块板子正在进行程序调试,则I2C总线可能刚启动就被暂停,则处于主模式的I2C模块迟迟收不到相应的应答或时钟响应,就会仲裁为出错,预定的中断不会产生,程序就会永远停在某个状态。所以,最好需要打开I2C模块的超时出错中断。

?
/*
********************************************************************************
? ? 模块说明
================================================================================
? ? 1. ?PA9 ? ? I2C_SCL
? ? ? ? PA10 ? ?I2C_SDA
? ? 2. ?配制为主机模式
********************************************************************************
*/
?
?
/*
********************************************************************************
? ? 内部宏
********************************************************************************
*/
// ?I2C宏定义
#define I2C_PPH_TYPE ? ? ? ?I2C1
#define I2C_APB_CMD ? ? ? ? RCC_APB1PeriphClockCmd
?
#define I2C_APB_CLK ? ? ? ? RCC_APB1Periph_I2C1
#define I2C_INT_HANDLER ? ? I2C1_IRQHandler
#define I2C_INT_IRQ ? ? ? ? I2C1_IRQn
//==============================================================================
#define I2C_BUF_LEN ? ? ? ? 32
//==============================================================================
#define ERROR_IT_MASK ? ? ? ((uint32_t)0x00003F00) ?/*<! I2C Error interrupt register Mask */
//==============================================================================
#define I2C_MEM_ADDR_FLG ? ?0x01 ? ? ? ?// ?内存地址标志
#define I2C_LO_ADDR_FLG ? ? 0x02 ? ? ? ?// ?地址低位
?
#define SET_MEM_ADDR() ? ? ?devStatusFlag |= ?I2C_MEM_ADDR_FLG
#define CLR_MEM_ADDR() ? ? ?devStatusFlag &= ~I2C_MEM_ADDR_FLG
#define GET_MEM_ADDR() ? ? (devStatusFlag & I2C_MEM_ADDR_FLG)
?
#define SET_LO_ADDR() ? ? ? devStatusFlag |= ?I2C_LO_ADDR_FLG
#define CLR_LO_ADDR() ? ? ? devStatusFlag &= ~I2C_LO_ADDR_FLG
#define GET_LO_ADDR() ? ? ?(devStatusFlag & I2C_LO_ADDR_FLG)
?
/*
********************************************************************************
? ? 内部变量
********************************************************************************
*/
__no_init static PHVV ? ?pI2cErrHandler; // ?操作出错回调函数指针
__no_init static PHVV ? ?pI2cOcHandler; ?// ?操作完成回调函数指针
?
__no_init static INT8U ?*pTrxAddr; ? ? ?// ?数据读写地址
__no_init static INT16U ?devSlaveAddr; ?// ?从设备地址
__no_init static INT16U ?devTrxNum; ? ? // ?数据收发个数
__no_init static INT16U ?devMemAddr; ? ?// ?设备内存地址
__no_init __IO static INT8U ? devStatusFlag; // ?设备运行状态
?
/*
********************************************************************************
? ? 内部常量
********************************************************************************
*/
// ?I2C驱动模块初始化结构
const static I2C_InitTypeDef ? ?I2cPph_InitStructure ?= ??
{
? ? .I2C_Mode ? ? ? ? ? = ? I2C_Mode_I2C,
? ? .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit,
? ? .I2C_AnalogFilter ? = ? I2C_AnalogFilter_Enable,
? ? .I2C_DigitalFilter ?= ? 0x00, ??
? ? .I2C_OwnAddress1 ? ?= ? 0x00,
? ? .I2C_Ack ? ? ? ? ? ?= ? I2C_Ack_Enable,
// ? ?.I2C_Timing ? ? ? ? = ? 0x10210507,
? ? .I2C_Timing ? ? ? ? = ? 0x30331012,
};
?
// ?I2C中断参数初始化结构
const static NVIC_InitTypeDef ? I2c_NvicInitStructure ? ? =
{
? ? .NVIC_IRQChannel ? ? ? ?= ? I2C_INT_IRQ,
? ? .NVIC_IRQChannelPriority = ?1,
? ? .NVIC_IRQChannelCmd ? ? = ? ENABLE
};
//******************************************************************************
?
?
?
?
?
?
?
?
?
?
?
?
/*
********************************************************************************
Name ? ?: ? I2cPphInit
Brief ? : ? 初始化I2C转换模块
Input ? : ? 无
Output ?: ? 无
================================================================================
********************************************************************************
*/
void I2cPphInit(void)
{ ? ?
? ? pI2cErrHandler ?= ? NULL;
? ? pI2cOcHandler ? = ? NULL;
? ? devStatusFlag ? = ? 0;
? ??
? ? I2C_APB_CMD(I2C_APB_CLK, ENABLE);
? ? RCC_I2CCLKConfig(RCC_I2C1CLK_SYSCLK);
? ? NVIC_Init((NVIC_InitTypeDef *)&I2c_NvicInitStructure);
? ? I2C_Init(I2C_PPH_TYPE, (I2C_InitTypeDef *)&I2cPph_InitStructure);
? ??
? ??
? ? I2C_Cmd(I2C_PPH_TYPE, ENABLE);
? ? I2C_TimeoutAConfig(I2C_PPH_TYPE,64);
? ? I2C_TimeoutBConfig(I2C_PPH_TYPE,64);
? ? I2C_ClockTimeoutCmd(I2C_PPH_TYPE, ENABLE);
// ? ?I2C_IdleClockTimeoutCmd(I2C_PPH_TYPE, ENABLE);
? ? I2C_ClearITPendingBit(I2C_PPH_TYPE, ERROR_IT_MASK);
? ? I2C_ITConfig(I2C_PPH_TYPE, I2C_IT_ERRI | I2C_IT_TCI | I2C_IT_STOPI | I2C_IT_RXI | I2C_IT_ADDRI | I2C_IT_TXI, ENABLE);
}
//******************************************************************************
?
?
/*
********************************************************************************
Name ? ?: ? I2cPphDeinit
Brief ? : ? 反初始化I2C转换模块
Input ? : ? 无
Output ?: ? 无
================================================================================
********************************************************************************
*/
void I2cPphDeinit(void)
{
? ? I2C_Cmd(I2C_PPH_TYPE, DISABLE); ?
}
//******************************************************************************
?
/*
********************************************************************************
Name ? ?: ? I2cPphWrite
Brief ? : ? 写操作
Input ? : ? pBuf ? ?待写缓存地址
? ? ? ? ? ? devAddr 设备地址
? ? ? ? ? ? memAddr 内存地址
? ? ? ? ? ? nLen ? ?数据个数
Output ?: ? 无
================================================================================
********************************************************************************
*/
void I2cPphWrite(INT8U *pBuf, INT8U devAddr, INT16U memAddr, INT16U nLen)
{
? ? pTrxAddr ? ?= ? pBuf;
? ? devMemAddr ?= ? memAddr;
? ? devSlaveAddr= ? devAddr;
? ? devTrxNum ? = ? nLen;
? ??
? ? SET_MEM_ADDR();
? ? CLR_LO_ADDR();
? ? I2C_ClearFlag(I2C_PPH_TYPE, I2C_ICR_NACKCF | I2C_ICR_STOPCF);
? ? I2C_TransferHandling(I2C_PPH_TYPE, devAddr, 2, I2C_Reload_Mode, I2C_Generate_Start_Write);
? ? I2C_GenerateSTART(I2C_PPH_TYPE, ENABLE); ? ? ?
}
//******************************************************************************
?
/*
********************************************************************************
Name ? ?: ? I2cPphRead
Brief ? : ? 读操作
Input ? : ? pBuf ? ?待写缓存地址
? ? ? ? ? ? devAddr 设备地址
? ? ? ? ? ? memAddr 内存地址
? ? ? ? ? ? nLen ? ?数据个数
Output ?: ? 无
================================================================================
********************************************************************************
*/
void I2cPphRead(INT8U *pBuf, INT8U devAddr, INT16U memAddr, INT16U nLen)
{
? ? pTrxAddr ? ?= ? pBuf;
? ? devMemAddr ?= ? memAddr;
? ? devSlaveAddr= ? devAddr;
? ? devTrxNum ? = ? nLen;
? ??
? ? SET_MEM_ADDR();
? ? CLR_LO_ADDR();
? ? I2C_ClearFlag(I2C_PPH_TYPE, I2C_ICR_NACKCF | I2C_ICR_STOPCF);
? ? I2C_TransferHandling(I2C_PPH_TYPE, devAddr, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
? ? I2C_GenerateSTART(I2C_PPH_TYPE, ENABLE); ? ? ?
}
//******************************************************************************
?
/*
********************************************************************************
Name ? ?: ? I2cPphCallbackSet
Brief ? : ? 回调函数设置
Input ? : ? pErrHandler ? ? 操作出错回调函数指针
? ? ? ? ? ? pOcHandler ? ? ?操作完成回调函数指针
Output ?: ? 无
================================================================================
********************************************************************************
*/
void I2cPphCallbackSet(PHVV pErrHandler, PHVV pOcHandler)
{
? ? pI2cErrHandler ?= ? pErrHandler;
? ? pI2cOcHandler ? = ? pOcHandler;
}
//******************************************************************************
?
/*
********************************************************************************
Name ? ?: ? I2cPphErrRoutine
Brief ? : ? 出错处理程序
Input ? : ??
Output ?: ? 无
================================================================================
********************************************************************************
*/
static void I2cPphErrRoutine(void)
{
// ? ? ? ?HAL_I2C_TYPE->CR1 |=0x2000;
? ? I2C_SoftwareResetCmd(I2C_PPH_TYPE);
? ? I2C_ClearITPendingBit(I2C_PPH_TYPE, ERROR_IT_MASK);
? ? ? ??
? ? if(ASSERT_PFN(pI2cErrHandler))
? ? {
? ? ? ? pI2cErrHandler();
? ? }
}
//******************************************************************************
?
?
?
?
?
?
?
?
?
?
/*
********************************************************************************
Name ? ?: ? I2C_INT_HANDLER
Brief ? : ? I2C中断处理函数
Input ? : ? 无
Output ?: ? 无
================================================================================
********************************************************************************
*/
void I2C_INT_HANDLER(void)
{
? ? INT16U ? ? ?flag = I2C_PPH_TYPE->ISR & 0x3FFE;
? ??
? ? if(flag & ERROR_IT_MASK)
? ? {// 操作出错
? ? ? ? I2cPphErrRoutine();
? ? }
? ? else
? ? {
? ? ? ? switch(flag)
? ? ? ? {
? ? ? ? case 0x0002: ? ?// ?写数据操作
? ? ? ? ? ? if(GET_MEM_ADDR())
? ? ? ? ? ? {
? ? ? ? ? ? ? ? if(GET_LO_ADDR())
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? I2C_PPH_TYPE->TXDR ?= ? devMemAddr;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? I2C_PPH_TYPE->TXDR ?= ? (INT8U)(devMemAddr>>8);
? ? ? ? ? ? ? ? ? ? SET_LO_ADDR();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? else
? ? ? ? ? ? {
? ? ? ? ? ? ? ? if(devTrxNum)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? I2C_PPH_TYPE->TXDR ?= ? *pTrxAddr++;
? ? ? ? ? ? ? ? ? ? devTrxNum--;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? I2cPphErrRoutine();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? break;
? ? ? ? case 0x0004: ? ?// ?读数据操作
? ? ? ? ? ? if(devTrxNum)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? *pTrxAddr++ = ? I2C_PPH_TYPE->RXDR;
? ? ? ? ? ? ? ? devTrxNum--;
? ? ? ? ? ? }
? ? ? ? ? ? else
? ? ? ? ? ? { ? ? ? ? ?
? ? ? ? ? ? ? ? I2cPphErrRoutine();
? ? ? ? ? ? }
? ? ? ? ? ? break;
? ? ? ? case 0x0040: ? ?// ?写地址完成,准备读数据 ??
? ? ? ? ? ? CLR_MEM_ADDR();
? ? ? ? ? ? I2C_TransferHandling(I2C_PPH_TYPE, devSlaveAddr, devTrxNum, I2C_AutoEnd_Mode, I2C_Generate_Start_Read); ??
? ? ? ? ? ? break;
? ? ? ? case 0x0080: ? ?// ?写地址完成,准备写数据
? ? ? ? ? ? CLR_MEM_ADDR();
? ? ? ? ? ? I2C_TransferHandling(I2C_PPH_TYPE, devSlaveAddr, devTrxNum, I2C_AutoEnd_Mode, I2C_No_StartStop);
? ? ? ? ? ? break;
? ? ? ? case 0x0020: ? ?// ?读结束
? ? ? ? case 0x0030: ? ?// ?写结束
? ? ? ? ? ? I2C_ClearITPendingBit(I2C_PPH_TYPE, I2C_IT_STOPF);
? ? ? ? ? ? if(ASSERT_PFN(pI2cOcHandler))
? ? ? ? ? ? {
? ? ? ? ? ? ? ? pI2cOcHandler();
? ? ? ? ? ? }
? ? ? ? ? ? break;
? ? ? ? default: ? ? ? ?// ?其他情况,判定为出错 ? ? ? ? ? ?
? ? ? ? ? ? I2cPphErrRoutine();
? ? ? ? ? ? break;
? ? ? ? }
? ? }
}
//******************************************************************************
?
?
// 写不下了,就此结束吧。
?
?

分享到:? 新浪微博?? qq空间??
投票数: ? 回复?? ? 引用??
1楼??[原创]?

Re:《STM32F0308-DISCO套件》使用心得---硬件中断驱动的I2C


作者:s1j2h3
积分:45分
总积分:166分
:836
发送信息
加为好友
发表于2015-11-16 17:03
本程序最好配合操作系统使用到,以达到用最少的时间做最多的事。简单一点的可以是Protothread。在发出读写命令后,就可以去忙别的事了。操作完成后,不管是否成功,总会有一个回调函数被调用,这时上层模块就可以接着处理后面的事了。

点击登录,立即回复。
小组活跃用户