TA的每日心情 | 开心 2013-7-2 13:29 |
|---|
签到天数: 1 天 连续签到: 1 天 [LV.1]初来乍到
举人
- 积分
- 706

|
本帖最后由 shen2008jie 于 2013-8-23 18:07 编辑
好了,那么接着徐州工程学院(开发手册)STM32之驱动移植杂记(2) 上一篇的具体延时函数构造讲解,下面接着
说一说延时函数在IIC的改造。
分割线--------------------------------------------------------------------------
对于那个_Nop();现在只要
宏定义:
#define Delay_us(1) _Nop()
就行了。
总结一下:这篇主要讲了配置系统定时器,实现准确延时。还更改了IIc的开始函数。
首先我们还是讲解这个代码更改:开始改这个函数代码:
/*******************************************************************
起动总线函数
函数原型: void Start_I2c();
功能: 启动I2C总线,即发送I2C起始条件.
********************************************************************/
void Start_I2c()
{
SDA2=1; /*发送起始条件的数据信号*/
_Nop();
SCK2=1;
_Nop(); /*起始条件建立时间大于4.7us,延时*/
_Nop();
_Nop();
_Nop();
_Nop();
SDA2=0; /*发送起始信号*/
_Nop(); /* 起始条件锁定时间大于4μs*/
_Nop();
_Nop();
_Nop();
_Nop();
SCK2=0; /*钳住I2C总线,准备发送或接收数据 */
_Nop();
_Nop();
}
从徐州工程学院(开发手册)STM32之驱动移植杂记(2)中我讲了一些更改,这里我就直接先给出更改后的代码:
/*******************************************************************
起动总线函数
函数原型: void Start_I2c();
功能: 启动I2C总线,即发送I2C起始条件.
********************************************************************/
void Start_I2c()
{
IIC_SDA_1; /*发送起始条件的数据信号*/
Delay_us(1) ;
IIC_SCL_1 ;
Delay_us(5); /*//_Nop(); 起始条件建立时间大于4.7us,延时
//_Nop();
// _Nop();
//_Nop();
//_Nop(); */
IIC_SDA_0 ; /*发送起始信号*/
Delay_us(5); /*//_Nop(); 起始条件锁定时间大于4μs
//_Nop();
// _Nop();
// _Nop();
// _Nop(); */
IIC_SCL_0 ; /*钳住I2C总线,准备发送或接收数据 */
Delay_us(2); //_Nop();
//_Nop();
}
文件头加上几句宏定义:
#define IIC_SCL_1 GPIO_SetBits(GPIOD, GPIO_Pin_0)
#define IIC_SCL_0 GPIO_ResetBits(GPIOD, GPIO_Pin_0)
#define IIC_SDA_1 GPIO_SetBits(GPIOD, GPIO_Pin_1)
#define IIC_SDA_0 GPIO_ResetBits(GPIOD, GPIO_Pin_1) //输入SDA
只给出需要的四个。
那么,同样的方法,我们改第二个函数:
C51的源代码:
/*===========================================结束总线函数 ================================================*/
/*******************************************************************
结束总线函数
函数原型: void Stop_I2c();
功能: 结束I2C总线,即发送I2C结束条件.
********************************************************************/
void Stop_I2c()
{
SDA2=0; /*发送结束条件的数据信号*/
_Nop(); /*发送结束条件的时钟信号*/
SCK2=1; /*结束条件建立时间大于4μs*/
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SDA2=1; /*发送I2C总线结束信号*/
_Nop();
_Nop();
_Nop();
_Nop();
}
改后:
/*===========================================结束总线函数 ================================================*/
/*******************************************************************
结束总线函数
函数原型: void Stop_I2c();
功能: 结束I2C总线,即发送I2C结束条件.
********************************************************************/
void Stop_I2c()
{
IIC_SDA_0 ; /*发送结束条件的数据信号*/
Delay_us(1); /*发送结束条件的时钟信号*/
IIC_SCL_1; /*结束条件建立时间大于4μs*/
Delay_us(5);
IIC_SDA_1; /*发送I2C总线结束信号*/
Delay_us(4);
}
好了这个没有什么难点。下面一个函数:
/*========================================== 字节数据发送函数 ================================================*/
/*******************************************************************
字节数据发送函数
函数原型: void SendByte(UCHAR c);
功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
此状态位进行操作.(不应答或非应答都使ack=0)
发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
********************************************************************/
void SendByte2(unsigned char c)
{
unsigned char BitCnt;
for(BitCnt=0;BitCnt<8;BitCnt++) /*要传送的数据长度为8位*/
{
if((c<<BitCnt)&0x80)SDA2=1; /*判断发送位*/
else SDA2=0;
_Nop();
SCK2=1; /*置时钟线为高,通知被控器开始接收数据位*/
_Nop();
_Nop(); /*保证时钟高电平周期大于4μs*/
_Nop();
_Nop();
_Nop();
SCK2=0;
}
_Nop();
_Nop();
SDA2=1; /*8位发送完后释放数据线,准备接收应答位*/
_Nop();
_Nop();
SCK2=1;
_Nop();
_Nop();
_Nop();
if(SDA2==1)ack=0;
else ack=1; /*判断是否接收到应答信号*/
SCK2=0;
_Nop();
_Nop();
}
大概看了程序,这里面的代码,与之前也没有什么变化,但是里面却有一个值得注意的问题,对于一些初学者。
if(SDA2==1)ack=0;
else ack=1; /*判断是否接收到应答信号*/
这一句是判断SDA是否是高电平,此语句是STM32读取外部器件信号,是管脚的外部输入模式。所以在
徐州工程学院(开发手册)STM32和zigbee技术文档杂记(1)中我讲到的管脚模式的选择就很重要。那么在第一篇中,我介绍了管脚设置的寄存器操作方式,这样又便于移植,贴出代码:
/* 宏定义 -------- -------- -------- -------- -------- -------- -------- -------- -------- -------- ----- */
//IO方向设置宏定义
#define SDA _pin 1
#define GPIOXX GPIOD
#define SDA_IN() {GPIOXX-> MODER &=~((0x00000003)<<(2* pinpos )); GPIOXX-> MODER |= (((uint32_t) 0x00 ) << ( SDA _pin * 2)); } //这里GPIO XX-> MODER选的是D的
#define SDA_OUT() {GPIOXX-> MODER &=~((0x00000003)<<(2* pinpos )); GPIOXX-> MODER |= (((uint32_t) 0x01 ) << ( SDA _pin * 2)); } //这里GPIO XX-> MODER选的是D的
#define READ_SDA GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_1)
那么SDA_IN() 这句就是设置为输入模式,SDA_OUT()就是输出模式。
那么我直接贴出更改后的函数:(初学者可以先浏览之前的帖子,那个讲的详细)
/*========================================== 字节数据发送函数 ================================================*/
/*******************************************************************
字节数据发送函数
函数原型: void SendByte(UCHAR c);
功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
此状态位进行操作.(不应答或非应答都使ack=0)
发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
********************************************************************/
void SendByte2(unsigned char c)
{
unsigned char BitCnt;
SDA_OUT() ;
for(BitCnt=0;BitCnt<8;BitCnt++) /*要传送的数据长度为8位*/
{
if((c<<BitCnt)&0x80) IIC_SDA_1 ; /*判断发送位*/
else IIC_SDA_0;
Delay_us(1);
IIC_SCL_1 ; /*置时钟线为高,通知被控器开始接收数据位*/
Delay_us(5); //_Nop(); /*保证时钟高电平周期大于4μs*/
IIC_SCL_0;
}
Delay_us(2);
IIC_SDA_1; /*8位发送完后释放数据线,准备接收应答位*/
Delay_us(2);
IIC_SCL_1 ;
Delay_us(3);
------//这里很关键
SDA_IN() ;
if( READ_SDA ==1)ack=0;
else ack=1; /*判断是否接收到应答信号*/
IIC_SCL_0 ;
Delay_us(2);
}
上方的 READ_SDA 是#define READ_SDA GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_1)
GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_1) 这个在库函数中就是在输入模式下读取管脚的高低函数。
下面IIC还有如下代码:
/*========================================== 字节数据接收函数 ================================================*/
/*******************************************************************
字节数据接收函数
函数原型: UCHAR RcvByte();
功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号),
发完后请用应答函数应答从机。
********************************************************************/
unsigned char RcvByte()
{
unsigned char retc;
unsigned char BitCnt;
retc=0;
SDA2=1; /*置数据线为输入方式*/
for(BitCnt=0;BitCnt<8;BitCnt++)
{
_Nop();
SCK2=0; /*置时钟线为低,准备接收数据位*/
_Nop();
_Nop(); /*时钟低电平周期大于4.7μs*/
_Nop();
_Nop();
_Nop();
SCK2=1; /*置时钟线为高使数据线上数据有效*/
_Nop();
_Nop();
retc=retc<<1;
if(SDA2==1)retc=retc+1; /*读数据位,接收的数据位放入retc中 */
_Nop();
_Nop();
}
SCK2=0;
_Nop();
_Nop();
return(retc);
}
/*========================================== 应答子函数 ================================================*/
/********************************************************************
应答子函数
函数原型: void Ack_I2c(bit a);
功能: 主控器进行应答信号(可以是应答或非应答信号,由位参数a决定)
********************************************************************/
void Ack_I2c(bit a)
{
if(a==0)SDA2=0; /*在此发出应答或非应答信号 */
else SDA2=1;
_Nop();
_Nop();
_Nop();
SCK2=1;
_Nop();
_Nop(); /*时钟低电平周期大于4μs*/
_Nop();
_Nop();
_Nop();
SCK2=0; /*清时钟线,钳住I2C总线以便继续接收*/
_Nop();
_Nop();
}
改代码的方式与之前一下。代码更改如下:
/*========================================== 字节数据接收函数 ================================================*/
/*******************************************************************
字节数据接收函数
函数原型: UCHAR RcvByte();
功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号),
发完后请用应答函数应答从机。
********************************************************************/
unsigned char RcvByte()
{
unsigned char retc;
unsigned char BitCnt;
retc=0;
SDA_IN() ;
IIC_SDA_1 ; /*置数据线为输入方式*/
for(BitCnt=0;BitCnt<8;BitCnt++)
{
Delay_us(1);
IIC_SCL_0; /*置时钟线为低,准备接收数据位*/
Delay_us(5); /*时钟低电平周期大于4.7μs*/
IIC_SCL_1 ; /*置时钟线为高使数据线上数据有效*/
Delay_us(2);
retc=retc<<1;
if(READ_SDA ==1)retc=retc+1; /*读数据位,接收的数据位放入retc中 */
Delay_us(2);
}
IIC_SCL_0;
Delay_us(2);
return(retc);
}
/*========================================== 应答子函数 ================================================*/
/********************************************************************
应答子函数
函数原型: void Ack_I2c(bit a);
功能: 主控器进行应答信号(可以是应答或非应答信号,由位参数a决定)
********************************************************************/
void Ack_I2c(bit a)
{
SDA_OUT();
if(a==0) IIC_SDA_0; /*在此发出应答或非应答信号 */
else IIC_SDA_1 ;
Delay_us(3);
IIC_SCL_1 ;
Delay_us(5); /*时钟低电平周期大于4μs*/
IIC_SCL_0; /*清时钟线,钳住I2C总线以便继续接收*/
Delay_us(2);
}
上面的两个函数中改代码是一定要注意,STM32管脚置高,或者清零时,是输入模式还是输出模式。
那么今天就讲完了IIC代码的移植。
今天要注意的就是管脚的输入输出模式。大家可以查一下管脚模式的各个意思。如推挽输出,等等。。。。。
最后总结一下代码:
/* 宏定义 -------- -------- -------- -------- -------- -------- -------- -------- -------- -------- ----- */
//IO方向设置宏定义
#define SDA _pin 1
#define GPIOXX GPIOD
#define SDA_IN() {GPIOXX-> MODER &=~((0x00000003)<<(2* pinpos )); GPIOXX-> MODER |= (((uint32_t) 0x00 ) << ( SDA _pin * 2)); } //这里GPIO XX-> MODER选的是D的
#define SDA_OUT() {GPIOXX-> MODER &=~((0x00000003)<<(2* pinpos )); GPIOXX-> MODER |= (((uint32_t) 0x01 ) << ( SDA _pin * 2)); } //这里GPIO XX-> MODER选的是D的
//IO操作函数宏定义
#define IIC_SCL_1 GPIO_SetBits(GPIOD, GPIO_Pin_0)
#define IIC_SCL_0 GPIO_ResetBits(GPIOD, GPIO_Pin_0)
#define IIC_SDA_1 GPIO_SetBits(GPIOD, GPIO_Pin_1)
#define IIC_SDA_0 GPIO_ResetBits(GPIOD, GPIO_Pin_1) //输入SDA
#define READ_SDA GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_1)//输入模式的读管脚高低
/* 初始化IIC -------- -------- -------- -------- -------- -------- -------- -------- -------- -------- ----- */
---------函数名 IIC_Init
---------输入无
---------输出无
---------
---------
/* 初始化IIC -------- -------- -------- -------- -------- -------- -------- -------- -------- -------- ----- */
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//悬空
GPIO_Init(GPIOB, &GPIO_InitStructure);
IIC_SCL_1;//管脚置高
IIC_SDA_1; //管脚置高
}
/*******************************************************************
起动总线函数
函数原型: void Start_I2c();
功能: 启动I2C总线,即发送I2C起始条件.
********************************************************************/
void Start_I2c()
{
IIC_SDA_1; /*发送起始条件的数据信号*/
Delay_us(1) ;
IIC_SCL_1 ;
Delay_us(5); /*//_Nop(); 起始条件建立时间大于4.7us,延时
//_Nop();
// _Nop();
//_Nop();
//_Nop(); */
IIC_SDA_0 ; /*发送起始信号*/
Delay_us(5); /*//_Nop(); 起始条件锁定时间大于4μs
//_Nop();
// _Nop();
// _Nop();
// _Nop(); */
IIC_SCL_0 ; /*钳住I2C总线,准备发送或接收数据 */
Delay_us(2); //_Nop();
//_Nop();
}
/*===========================================结束总线函数 ================================================*/
/*******************************************************************
结束总线函数
函数原型: void Stop_I2c();
功能: 结束I2C总线,即发送I2C结束条件.
********************************************************************/
void Stop_I2c()
{
IIC_SDA_0 ; /*发送结束条件的数据信号*/
Delay_us(1); /*发送结束条件的时钟信号*/
IIC_SCL_1; /*结束条件建立时间大于4μs*/
Delay_us(5);
IIC_SDA_1; /*发送I2C总线结束信号*/
Delay_us(4);
}
/*========================================== 字节数据发送函数 ================================================*/
/*******************************************************************
字节数据发送函数
函数原型: void SendByte(UCHAR c);
功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
此状态位进行操作.(不应答或非应答都使ack=0)
发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
********************************************************************/
void SendByte2(unsigned char c)
{
unsigned char BitCnt;
SDA_OUT() ;
for(BitCnt=0;BitCnt<8;BitCnt++) /*要传送的数据长度为8位*/
{
if((c<<BitCnt)&0x80) IIC_SDA_1 ; /*判断发送位*/
else IIC_SDA_0;
Delay_us(1);
IIC_SCL_1 ; /*置时钟线为高,通知被控器开始接收数据位*/
Delay_us(5); //_Nop(); /*保证时钟高电平周期大于4μs*/
IIC_SCL_0;
}
Delay_us(2);
IIC_SDA_1; /*8位发送完后释放数据线,准备接收应答位*/
Delay_us(2);
IIC_SCL_1 ;
Delay_us(3);
------//这里很关键
SDA_IN() ;
if( READ_SDA ==1)ack=0;
else ack=1; /*判断是否接收到应答信号*/
IIC_SCL_0 ;
Delay_us(2);
}
/*========================================== 字节数据接收函数 ================================================*/
/*******************************************************************
字节数据接收函数
函数原型: UCHAR RcvByte();
功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号),
发完后请用应答函数应答从机。
********************************************************************/
unsigned char RcvByte()
{
unsigned char retc;
unsigned char BitCnt;
retc=0;
SDA_IN() ;
IIC_SDA_1 ; /*置数据线为输入方式*/
for(BitCnt=0;BitCnt<8;BitCnt++)
{
Delay_us(1);
IIC_SCL_0; /*置时钟线为低,准备接收数据位*/
Delay_us(5); /*时钟低电平周期大于4.7μs*/
IIC_SCL_1 ; /*置时钟线为高使数据线上数据有效*/
Delay_us(2);
retc=retc<<1;
if( READ_SDA ==1)retc=retc+1; /*读数据位,接收的数据位放入retc中 */
Delay_us(2);
}
IIC_SCL_0;
Delay_us(2);
return(retc);
}
/*========================================== 应答子函数 ================================================*/
/********************************************************************
应答子函数
函数原型: void Ack_I2c(bit a);
功能: 主控器进行应答信号(可以是应答或非应答信号,由位参数a决定)
********************************************************************/
void Ack_I2c(bit a)
{
SDA_OUT();
if(a==0) IIC_SDA_0; /*在此发出应答或非应答信号 */
else IIC_SDA_1 ;
Delay_us(3);
IIC_SCL_1 ;
Delay_us(5); /*时钟低电平周期大于4μs*/
IIC_SCL_0; /*清时钟线,钳住I2C总线以便继续接收*/
Delay_us(2);
}
声明:全篇都是手敲的,原创写的,有错误见谅。 |
|