7回答

1收藏

徐州工程学院(开发手册)STM32和zigbee技术文档记3

#竞赛 #竞赛 3365 人阅读 | 7 人回复 | 2013-08-23

本帖最后由 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);  
}


声明:全篇都是手敲的,原创写的,有错误见谅。
分享到:
回复

使用道具 举报

回答|共 7 个

倒序浏览

沙发

shamowangzi

发表于 2013-8-23 18:11:34 | 只看该作者

沙发,支持一个!LZ辛苦了!
期待连载!!
板凳

ssj-413971

发表于 2013-8-23 18:17:31 | 只看该作者

shamowangzi 发表于 2013-8-23 18:11
沙发,支持一个!LZ辛苦了!
期待连载!!

有空就会在这码代码,最近都会更新。
地板

哇哈哈0608

发表于 2013-8-23 18:40:20 | 只看该作者

为啥不把硬件IIC调试出来呢
5#

ssj-413971

发表于 2013-8-23 18:52:54 | 只看该作者

哇哈哈0608 发表于 2013-8-23 18:40
为啥不把硬件IIC调试出来呢

硬件IIC根据官方例程是很简单,只要按照官方的函数,按照开时钟,配置结构体,初试化函数调用,使能就可以使用了。我写这个移植主要是当你有某个模块时可以根据已有代码,移植到这个开发板上,并不是只是说模拟一个IIC。
6#

sixyj

发表于 2013-8-23 19:08:35 | 只看该作者

楼主加油!期待连载。。{:soso_e142:}
7#

zhaolinsheng

发表于 2013-8-23 20:20:25 | 只看该作者

楼主这块很牛啊
8#

哇哈哈0608

发表于 2013-8-23 23:46:10 | 只看该作者

shen2008jie 发表于 2013-8-23 18:52
硬件IIC根据官方例程是很简单,只要按照官方的函数,按照开时钟,配置结构体,初试化函数调用,使能就可以 ...

呵呵    楼主威武,其实我一直都用软件模拟的方式,只不过最近一段时间我在调硬件的通信接口,感觉和模拟方式还是有区别的,所以这样问问。
您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条