6回答

0收藏

徐州工程学院(开发手册)STM32之驱动移植杂记(2)

#竞赛 #竞赛 4307 人阅读 | 6 人回复 | 2013-08-20

本帖最后由 shen2008jie 于 2013-8-20 13:15 编辑

好了接着昨天的写了的宏定义和一个初始化函数:
/* 宏定义 -------- -------- -------- -------- -------- -------- -------- -------- -------- -------- ----- */
//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; //管脚置高
}


今天继续移植模拟的IIC也就是C51中的代码使用到STM32中。
徐州工程学院(开发手册)STM32和zigbee技术文档杂记(1)中已经给出C51IIC的管脚的模拟代码
首先我们开始改这个函数代码:
/*******************************************************************
                     起动总线函数               
函数原型: 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();
}
这个事IIc的启动头信号代码。代码中有五个事件:SDA2=1; _Nop();SCK2=1; SDA2=0; SCK2=0;
分析这五个代码
SDA2=1;
这一句很容就明白是SDA置高,并且是由STM32给芯片信号,也就是输出模式所以根据昨天的STM32F4的解析
看上面的宏定义后就知道改成
IIC_SDA_1;
类似的SCK2=1; SDA2=0; SCK2=0;改成
IIC_SCL_1; IIC_SDA_0 IIC_SCL_0;
好了,那么就要解决_Nop();了
由于这是移植,不是优化,所以我们完全将其代码原封不动的移植过来,所以时间上也调成一样的。
在C51中代码时间约为1US。所以在STM32中设置1US的延迟
————————————止步
这篇的核心就将怎么写具体的延时函数。
下面有事一个较为麻烦的事儿。对于不准确的延时,只要空跑代码就行。看时钟就行,但是这里我将只用较为准确的延时。
这里就涉及到STM32的
在core_cm4.h文件中
/** \brief  Structure type to access the System Timer (SysTick). */
typedef struct
{
  __IO uint32_t CTRL;                    /*!< Offset: 0x000 (R/W)  SysTick Control and Status Register */
  __IO uint32_t LOAD;                    /*!< Offset: 0x004 (R/W)  SysTick Reload Value Register       */
  __IO uint32_t VAL;                     /*!< Offset: 0x008 (R/W)  SysTick Current Value Register      */
  __I  uint32_t CALIB;                   /*!< Offset: 0x00C (R/ )  SysTick Calibration Register        */
} SysTick_Type;

系统定时器,其中有四个参数:控制状态寄存器,重载寄存器,当前值寄存器,校准寄存器

那么我们先用做的就是SYSCLK初始化①:系统时钟选用什么时钟源。
在misc.h中有代码:
/** @defgroup MISC_SysTick_clock_source
  * @{
  */


#define SysTick_CLKSource_HCLK_Div8    ((uint32_t)0xFFFFFFFB)
#define SysTick_CLKSource_HCLK         ((uint32_t)0x00000004)
#define IS_SYSTICK_CLK_SOURCE(SOURCE) (((SOURCE) == SysTick_CLKSource_HCLK) || \
                                       ((SOURCE) == SysTick_CLKSource_HCLK_Div8))

还有下方有这个函数
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);
查看这个函数:
/**
  * @brief  Configures the SysTick clock source.
  * @param  SysTick_CLKSource: specifies the SysTick clock source.
  *   This parameter can be one of the following values:
  *     @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source.
  *     @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source.
  * @retval None
  */
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
{
  /* Check the parameters */
  assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));
  if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
  {
    SysTick->CTRL |= SysTick_CLKSource_HCLK;
  }
  else
  {
    SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
  }
}

SysTick_CLKSource_HCLK_Div8: AHB时钟除以8选为SysTick时钟源。
SysTick_CLKSource_HCLK:SysTick AHB时钟选为时钟源。
那我们就在初始化函数中选用这个函数SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);        //选择外部时钟HCLK/8
SYSTICK的时钟固定为HCLK时钟的1/8
那么初始化函数大概可以这样写:
void SysTick_Delay_init()
{
     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
//选择外部时钟HCLK/8   SYSTICK的时钟固定为HCLK时钟的1/8
   
}
但是由于系统时钟是可以设置的,那么就设置一个输入参数,这样就不局限了。
void SysTick_Delay_init(u8 Sys_clk)//Sys_clk单位是M。
{
     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
//选择外部时钟HCLK/8   SYSTICK的时钟固定为HCLK时钟的1/8
   
}
Sys_clk输入参数也影响到us、ms的装载的值。所以还要设置一个中间静态变量static u8  MID_us=0;、
static u16 MID_ms=0;//us、ms延时装载时要乘上的数
那么改初始化程序为:
void SysTick_Delay_init(u8 Sys_clk)//Sys_clk单位是M。
{
     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
//选择外部时钟HCLK/8   SYSTICK的时钟固定为HCLK时钟的1/8
    MID_us=Sys_clk/8;        // 由于八分频所以除以8         MID_ms=(u16)MID_us*1000;//一毫秒等于1000微妙
}
那么对于ms:由于SysTick->LOAD为24位寄存器,所以,最大延时为:MAX_ms<=0xffffff*8*1000/(Sys_Ck*10^6)
Sys_Ck单位为M,MAX_ms单位为ms
//对72M条件下,MAX_ms<=1864.135

那么写一个延时函数:这里原理就是51中设置预装定制值,开定时器,寻查到点的标志位,然后跳出循环,清除标志和装定制,就OK了。
那么写出代码:
计数器记一次就是8/(Sys_Ck*10^6)秒。对于72M那么Sys_Ck=72.
那么现在就可以知道为什么要有
     MID_us=Sys_clk/8;        // 由于八分频所以除以8         MID_ms=(u16)MID_us*1000;//一毫秒等于1000微妙

这两个数,这两个数乘上计数器一次的值就等于1.
假设我要延时一毫秒,那么装载的数值为0.001/{8/(Sys_Ck*10^6)}=Sys_clk/8*1000== MID_ms;
那么下面就是延时代码:
void Delay_ms(u16 ms)
{                                    
        u32 t;                  
        SysTick->LOAD=(u32)ms*MID_ms;//时间加载(SysTick->LOAD为24bit)
        SysTick->VAL =0x00;           //清空当前值计数器
        SysTick->CTRL=0x01 ;          //开始倒数  
        do
        {
                t=SysTick->CTRL;
        }
        while(!(t&(1<<16))&&t&0x01);//判断时间是否到达   
        SysTick->CTRL=0x00;       //清除控制寄存器
        SysTick->VAL =0X00;       //清空当前值                     
}   

类似的US的代码:复制改一下MID_us,us。
void Delay_us(u16 us)
{                                    
        u32 t;                  
        SysTick->LOAD=(u32)us*MID_us;//时间加载(SysTick->LOAD为24bit)
        SysTick->VAL =0x00;           //清空当前值计数器
        SysTick->CTRL=0x01 ;          //开始倒数  
        do
        {
                t=SysTick->CTRL;
        }
        while(!(t&(1<<16))&&t&0x01);//判断时间是否到达   
        SysTick->CTRL=0x00;       //清除控制寄存器
        SysTick->VAL =0X00;       //清空当前值                     
}   

那么在函数中建一个.h和.c包含以下就行了

那么延时一毫秒只要Delay_us(1);
分割线————————————————————
对于那个_Nop();现在只要
宏定义:
#define    Delay_us(1)    _Nop()
就行了。
总结一下:这篇主要讲了配置系统定时器,实现准确延时。还更改了IIc的开始函数。
今天就到这里。





今天由于得到一个很不好的消息,我的大妈突然得病住院,而且情形很严重,很严重。
,可能这一段时间没心情也没时间写,所以更新会慢一点了。



PCF8591与STM.jpg (129.08 KB, 下载次数: 60)

PCF8591与STM.jpg
分享到:
回复

使用道具 举报

回答|共 6 个

倒序浏览

沙发

zhaojunlin-1847508

发表于 2013-8-20 17:37:28 | 只看该作者

沙发~楼主代码解析的挺好的啊~~~~
祝家人身体健康
板凳

richardglx

发表于 2013-8-23 08:30:41 | 只看该作者

不错不错哒  
地板

suyong_yq

发表于 2014-1-16 12:48:56 | 只看该作者

这块片子上没有硬件的IIC么?不过据说STM32的IIC确实是让很多人纠结的。
代码解释得很详细,赞一个!
5#

shanghairen

发表于 2014-4-18 10:08:10 | 只看该作者

看你的贴,很享受,很充实   谢谢楼主
6#

ssj-413971

发表于 2014-4-18 10:51:34 | 只看该作者

shanghairen 发表于 2014-4-18 10:08
看你的贴,很享受,很充实   谢谢楼主

呵呵,谢谢啊
7#

shanghairen

发表于 2014-4-21 09:42:52 | 只看该作者

shen2008jie 发表于 2014-4-18 10:51
呵呵,谢谢啊

客气客气。多多好贴,让大家一起学习
您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

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