OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4369|回复: 1

STM32F103硬件IIC2寄存器版本-调通-无需中断DMA-目前四轴上运行良好-速度是模拟1.8倍

[复制链接]

1

主题

2

帖子

0

精华

新手上路

积分
22
金钱
22
注册时间
2020-5-1
在线时间
5 小时
发表于 2020-5-1 18:22:31 | 显示全部楼层 |阅读模式
#include "config.h"
#if defined(STM32_IIC)//如果采用硬件IIC

///*******************************************************************************************************
//        解决busy死锁问题--但是注意--不能中断打断IIC,否则会导致硬件错误中断
//********************************************************************************************************/
u8         IS_BUSY(void)
{
  u8 i;
  u32 timeOut=1000;

  while((I2C2->SR2&(1<<1))!=0)//总线忙BUSY!=0就在这里循环等待
               
         {
          if(timeOut==0)//假如不给超时只是循环的初始化IIC会导致无错误标志位的错误中断多次触发,标志位被PE清除导致
                        {
                                RCC->APB2ENR|=1<<3;       //PORTB时钟使能                         
                                //这里只针对IIC2引脚初始化
                                GPIOB->CRH&=0XFFFF00FF;
                                GPIOB->CRH|=0X00007700;//PB10-SCK PB11-SDA IO开漏输出0X77
                                for(i=0;i<10;i++)
                                {
                                PBout(10)=~PBout(10);//解决BUSY死锁问题
                                delay_us(20);
                                }
                               
                                PCout(13)=0;//LED亮起来

                                IIC2_Init();
                               
                                return 1;
                         }
                       
                        else
                               
                          timeOut--;
               
          }
       
  return 0;
       
}


void  IIC2_Init(void)
{
       
        RCC->APB1ENR|=1<<22;      //硬件IIC2的时钟使能
                                          //注意!!AFIO时钟无所谓开启,只有操作AF(复用功能)的IO重新映射,AFIO时钟才需要开启,并非属于普通IO时钟管理。
        RCC->APB2ENR|=1<<3;       //PORTB时钟使能                         
        //这里只针对IIC2引脚初始化
        GPIOB->CRH&=0XFFFF00FF;
        GPIOB->CRH|=0X0000FF00;//PB10-SCK PB11-SDA 全部需要开漏复用输出0XF

        //以下是IIC2的寄存器配置

        RCC->APB1RSTR|=1<<22;   //复位IIC2寄存器
        RCC->APB1RSTR&=~(1<<22);//停止复位IIC2寄存器       


        I2C2->CR1|=1<<15;//复位IIC2
        I2C2->CR1&=~(1<<15);
        delay_us(5);

        I2C2->CR1&=~(1<<1);//bit-1位   0:I2C模式    1:SMBus模式---配置为IIC模式。
        I2C2->CR1|=1<<10;  //bit-10位 0:无应答返回 1:接受到一个字节后返回一个应答(匹配的地址或数据)

        //I2C2->CR2|=1<<10;//bit-10位  ---缓冲器中断使能
        //I2C2->CR2|=1<<9;//bit-9位    ---事件中断使能
        I2C2->CR2|=1<<8;//bit-8位    ---出错中断使能
        I2C2->CR2|=0x24;//bit-5:0位  ---配置为输入时钟36M。

        I2C2->OAR1&=~(1<<15);//bit-15位  0:7位从地址   1:10位从地址---配置为7位从地址。
        I2C2->OAR1|=1<<14;   //bit-14位   ---要求必须软件保持‘1’
        I2C2->OAR1|=0x00;    //bit-7:1位  ---7位从模式地址设为0X00-接收主机信号使用

        I2C2->OAR2&=~(1);//bit-0位  0:7位地址模式只识别OAR1  1:7位地址模式下识别OAR1-2双地址--7位地址模式只识别OAR1。

        I2C2->CCR|=1<<15;   //bit-15位   ---配置为快速模式的IIC 400kHZ-所有配置寄存器最好在CR1中的PE使能位关闭的情况设置。
        I2C2->CCR&=~(1<<14);//bit-14位   ---快速模式时的占空比设置为2:1
        I2C2->CCR|=0x1F;    //bit-11:0位   ---配置为快速模式的IIC CCR为0x1F=31,2*31*27ns+1*31*27ns=2511ns一个周期,398.24771kHZ 无法达到400khz

        I2C2->TRISE=0xB;    //bit-5:0位   ---配置最大上升时间为277ns


        ///************** IIC2 NVIC 中断优先级配置*************************/  

        //使能错误中断       

        NVIC_Init(2,0,I2C2_ER_IRQn,2);//组2,分组不能变  

        I2C2->CR1|=1; //bit-0位--所有配置寄存器配置妥当后开启PE位---使能IIC2

        delay_ms(20);//这个20ms准备时间很重要,否则会上电IIC会进入一次错误中断,如果错误中断中有初始化IIC会循环错误中断。

}

#define TimeOut  1000

u8  IIC2_SingleWrite(uint8_t address, uint8_t subAddress, uint8_t data)
{  
         u16 SR2_STATE=0,i=0;
       
         if(IS_BUSY())return 1;

         I2C2->CR1|=1<<8; //产生起始条件  START=1;
   i=TimeOut;while((I2C2->SR1&(0x0001))!=0x0001)
   {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;}//SB=1 起始条件已发--读取SR1+写DR可清除。
       
         I2C2->DR=((address<<1)|0);//发送器件地址+写命令
       
         i=TimeOut;while((I2C2->SR1&(0x0002))!=0x0002)
   {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;}//ADDR=1代表地址发送成功, 收到从机ACK后置位1。     
         SR2_STATE=I2C2->SR2;//已读SR1-再读-SR2清除ADDR。
   SR2_STATE=SR2_STATE;
        /*注意,手册说在发送地址阶段不会设置TxE,所以这里TxE=0,不要通过判断TxE=1来确定收到ACK*/
               
         I2C2->DR=subAddress; //发出第一个DATA,这时候一写入DR马上进入移位寄存器,TxE还是为1。
               
   i=TimeOut;while((I2C2->SR1&(0x0080))!=0x0080)
   {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;}//这里等待TxE=1是为了判断DR是否为空。
               
         I2C2->DR=data;//发送数据--如果不行就考虑连续写2次DR测试下。
               
   i=TimeOut;while((I2C2->SR1&(0x0084))!=0x0084)
   {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;} //TxE=1且BTF=1 数据寄存器空--移位寄存器已经排空。
               
         I2C2->CR1|=1<<9; //        产生停止条件         STOP=1;          

         return 0;
}


  u8 IIC2_MultipleRead(uint8_t address, uint8_t subAddress, uint8_t * pBuf, uint8_t len)
{  
         u16 SR2_STATE=0,i=0;
         
         if(IS_BUSY())return 1;

         I2C2->CR1|=1<<8; //产生起始条件  START=1;
         i=TimeOut;while((I2C2->SR1&(0x0001))!=0x0001)
   {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;}//SB=1 起始条件已发--读取SR1+写DR可清除。
       
         I2C2->DR=((address<<1)|0); //发送器件地址+写命令
       
         i=TimeOut;while((I2C2->SR1&(0x0002))!=0x0002)
   {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;}//ADDR=1代表地址发送成功, 收到从机ACK后置位1。
         SR2_STATE=I2C2->SR2;//已读SR1-再读-SR2清除ADDR。
   SR2_STATE=SR2_STATE;
        /*注意,手册说在发送地址阶段不会设置TxE,所以这里TxE=0,不要通过判断TxE=1来确定收到ACK*/
               
         I2C2->DR=subAddress; //发出第一个DATA,这时候一写入DR马上进入移位寄存器,TxE还是为1
               
         i=TimeOut;while((I2C2->SR1&(0x0084))!=0x0084)
   {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;} //TxE=1且BTF=1 数据寄存器空--移位寄存器已经排空
               

   /*RESTART-中间插入重开始信号-手册只说10位地址有效-存在错误*/
                 
       
         I2C2->CR1|=1<<8; //重开始信号-起始条件  START=1;
         i=TimeOut;while((I2C2->SR1&(0x0001))!=0x0001)
   {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;}//SB=1 起始条件已发--读取SR1+写DR可清除。
       
         I2C2->DR=((address<<1)|1);//发送器件地址+读命令               
         
         I2C2->CR1|=1<<10;//开启应答-------->>       
         i=TimeOut;while((I2C2->SR1&(0x0002))!=0x0002)
   {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;}//等待地址发送成功。ADDR=1代表地址发送成功, 收到从机发出的接收地址完成ACK后置位1      
         SR2_STATE=I2C2->SR2;//已读SR1-再读-SR2清除ADDR-代表收到地址ACK

                 
         while(len)
                {
                        if(len==1)
                               
                        {  
                                 I2C2->CR1&=~(1<<10);//NACK
                               
                                 I2C2->CR1|=1<<9; //        请求产生停止条件STOP=1,注意位置-手册所需要当前字节传输完成ACK之前--执行停止请求后不要设置CR1
                       
                                 i=TimeOut;while((I2C2->SR1&(0x0040))!=0x0040)
         {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;}// RxNE=1 数据寄存器接收到数据  
                                 
                                 *pBuf=I2C2->DR;//读数据,发送nACK
                                         
                        }
                               
                        else
                        {
                               
                                 i=TimeOut;while((I2C2->SR1&(0x0040))!=0x0040)
         {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;}// RxNE=1 数据寄存器接收到数据  
                               
                                *pBuf=I2C2->DR;                //读数据,发送ACK
                                       
                        }
                       
                                len--;
                                pBuf++;
                }   
               
   return 0;                  
}

  u8 IIC2_MultipleWrite(uint8_t address, uint8_t subAddress, uint8_t * pBuf, uint8_t len)
{  
         u16 SR2_STATE=0,i=0;
         
         if(IS_BUSY())return 1;

         I2C2->CR1|=1<<8; //产生起始条件  START=1;
         i=TimeOut;while((I2C2->SR1&(0x0001))!=0x0001)
   {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;}//SB=1 起始条件已发--读取SR1+写DR可清除
       
         I2C2->DR=((address<<1)|0); //发送器件地址+写命令
       
         i=TimeOut;while((I2C2->SR1&(0x0002))!=0x0002)
   {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;}//ADDR=1代表地址发送成功     
         SR2_STATE=I2C2->SR2;//已读SR1-再读-SR2清除ADDR。
   SR2_STATE=SR2_STATE;
        /*注意,手册说在发送地址阶段不会设置TxE,所以这里TxE=0,不要通过判断TxE=1来确定收到ACK*/
               
         I2C2->DR=subAddress; //发出第一个DATA,这时候一写入DR马上进入移位寄存器,TxE还是为1
                 
   i=TimeOut;while((I2C2->SR1&(0x0080))!=0x0080)
   {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;}//这里等待TxE=1是为了判断DR是否为空。                 
               

         while(len)
                {
                        if(len==1)
                               
                        {  
                                 I2C2->DR=*pBuf;//发送最后一个Buf的数据
                    
         i=TimeOut;while((I2C2->SR1&(0x0084))!=0x0084)
         {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;} //TxE=1且BTF=1 数据寄存器空--移位寄存器已经排空
       
                                 I2C2->CR1|=1<<9; //        请求产生停止条件STOP=1--执行停止请求后不要设置CR1---->>
                                                 
                        }
                               
                        else
                        {
                               
                        I2C2->DR=*pBuf;//发送buff的数据
                               
                        i=TimeOut;while((I2C2->SR1&(0x0080))!=0x0080)
      {if(i==0){I2C2->CR1|=1<<9;return 1;}else i--;}//这里等待TxE=1是为了判断DR是否为空。
                       
                        }
                       
                        len--;
                       
                        pBuf++;
                }   
               
        return 0;          
}
///*******************************************************************************************************
//** 函数名称: IIC2_ER_IRQHandler
//** 功能描述: IIC2错误中断服务
//** 返    回: 无
//********************************************************************************************************/
void I2C2_ER_IRQHandler(void)
{   
  u8 i;

        for(i=0;i<3;i++)
        {
        PCout(13)=~PCout(13);//led闪起来
        delay_ms(50);
        }

        I2C2->SR1&=0x00FF;//软件写0清除
}

以上是本人详细啃手册加勘误手册一一解决相应问题而完善的驱动-目前在小四轴运行中良好-尤其是再也不用担心传感器数据太多而导致读取时间太长影响到姿态解算频率了。
目前本人没有发现新的问题--只要不要打断传输数据的过程,否则可能出现硬件错误-因为打断也许会导致传输数据的溢出行为--放在中断中执行完全没有问题-如有问题联系我,QQ545168596  wx:ndzdswkl



正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

286

帖子

0

精华

高级会员

Rank: 4

积分
950
金钱
950
注册时间
2018-4-21
在线时间
264 小时
发表于 2020-5-18 15:39:45 来自手机 | 显示全部楼层
谢谢分享。注释很皮哈,led亮起来 闪起来
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-29 01:24

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表