STM32使用模拟I2C读取AS5600:一步一步带你看波形图

一、了解I2C

首先我们要了解I2C的基本原理

当IIC处于空闲状态的时候,SDA和SCL都处于高电平状态,

当IIC通信开始信号,SCL保持高电平,SDA从高电平变成低电平(SCL=1,SDA=1->0),

当IIC通信结束信号,SCL保持高电平,SDA从低电平变成高电平(SCL=1,SDA=0->1)。

IIC通信开始后,发送8位数据信号,SCL拉低,

SDA发送数据最高位(7:1则高电平,0则低电平),然后SDA保持不变直到SCL下次低电平;

SDA发送数据(改变电平)后,SCL拉高,接收端读取信号后SCL再拉低。

共8次发送8个数据位【7:0】

 

 在发送8个数据后,输出端的SDA将释放总线,交给接收端来控制;

SCL拉低,输出端SDA释放总线,接收端如果应答:SDA拉低    不应答:SDA拉高

 

二、AS5600的读取

AS5600是12位的霍尔磁编码器,它的地址是0x36,只需要读取0x0C、0x0D这两个寄存器就可以读出角度的原始数据,再将其乘以360,再除以4096,就可以获得角度值。

 1.配置引脚

我们使用io模拟IIC通信,使用的是PB6,PB7;

这里我们直接使用正点原子的F103 IIC实验代码;

 

首先将sys.h和sys.c复制到我们的程序中(我使用的是模板,带oled驱动,方便调试)

sys两个文件程序我放在文章后面,方便没有下载正点原子例程的读者;

将myiic.h复制过来,再加上三个函数,再定义一下0x0c和0x0d两个寄存器地址;

#ifndef _AS5600_H
#define _AS5600_H

#include "sys.h"


//IO方向设置
 
#define SDA_IN()  {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;} //SDA切换为输入模式 
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;} //SDA切换为输出模式 

//IO操作函数	 
#define IIC_SCL    PBout(6) //SCL输出
#define IIC_SDA    PBout(7) //SDA输出	 
#define READ_SDA   PBin(7)  //SDA输入

#define	_raw_ang_hi 0x0c
#define	_raw_ang_lo 0x0d

//IICËùÓвÙ×÷º¯Êý
void IIC_Init(void);                //³õʼ»¯IICµÄIO¿Ú				 
void IIC_Start(void);				//·¢ËÍIIC¿ªÊ¼ÐźÅ
void IIC_Stop(void);	  			//·¢ËÍIICÍ£Ö¹ÐźÅ
void IIC_Send_Byte(u8 txd);			//IIC·¢ËÍÒ»¸ö×Ö½Ú
u8 IIC_Read_Byte(unsigned char ack);//IIC¶ÁÈ¡Ò»¸ö×Ö½Ú
u8 IIC_Wait_Ack(void); 				//IICµÈ´ýACKÐźÅ
void IIC_Ack(void);					//IIC·¢ËÍACKÐźÅ
void IIC_NAck(void);				//IIC²»·¢ËÍACKÐźÅ

void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);	  


u8 AS5600_ReadOneByte(u16 ReadAddr);
void AS5600_WriteOneByte(u16 WriteAddr,u8 WriteData);
u16 AS5600_ReadTwoByte(u16 ReadAddr_hi,u16 ReadAddr_lo);



#endif
#include "as5600.h"
#include "Delay.h"

 
//³õʼ»¯IIC
void IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	//ʹÄÜGPIOBʱÖÓ
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //ÍÆÍìÊä³ö
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); 	//PB6,PB7 Êä³ö¸ß
}
//²úÉúIICÆðʼÐźÅ
void IIC_Start(void)
{
	SDA_OUT();     //sdaÏßÊä³ö
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	Delay_us(4);
 	IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	Delay_us(4);
	IIC_SCL=0;//ǯסI2C×ÜÏߣ¬×¼±¸·¢ËÍ»ò½ÓÊÕÊý¾Ý 
}	  
//²úÉúIICÍ£Ö¹ÐźÅ
void IIC_Stop(void)
{
	SDA_OUT();//sdaÏßÊä³ö
	IIC_SCL=0;
	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	Delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;//·¢ËÍI2C×ÜÏß½áÊøÐźÅ
	Delay_us(4);
}
//µÈ´ýÓ¦´ðÐźŵ½À´
//·µ»ØÖµ£º1£¬½ÓÊÕÓ¦´ðʧ°Ü
//        0£¬½ÓÊÕÓ¦´ð³É¹¦
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      //SDAÉèÖÃΪÊäÈë  
	IIC_SDA=1;Delay_us(1);	   
	IIC_SCL=1;Delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//ʱÖÓÊä³ö0 	   
	return 0;  
} 
//²úÉúACKÓ¦´ð
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	Delay_us(2);
	IIC_SCL=1;
	Delay_us(2);
	IIC_SCL=0;
}
//²»²úÉúACKÓ¦´ð		    
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	Delay_us(2);
	IIC_SCL=1;
	Delay_us(2);
	IIC_SCL=0;
}					 				     
//IIC·¢ËÍÒ»¸ö×Ö½Ú
//·µ»Ø´Ó»úÓÐÎÞÓ¦´ð
//1£¬ÓÐÓ¦´ð
//0£¬ÎÞÓ¦´ð			  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL=0;//À­µÍʱÖÓ¿ªÊ¼Êý¾Ý´«Êä
    for(t=0;t<8;t++)
    {              
        //IIC_SDA=(txd&0x80)>>7;
		if((txd&0x80)>>7)
			IIC_SDA=1;
		else
			IIC_SDA=0;
		txd<<=1; 	  
		Delay_us(2);   //¶ÔTEA5767ÕâÈý¸öÑÓʱ¶¼ÊDZØÐëµÄ
		IIC_SCL=1;
		Delay_us(2); 
		IIC_SCL=0;	
		Delay_us(2);
    }	 
} 	    
//¶Á1¸ö×Ö½Ú£¬ack=1ʱ£¬·¢ËÍACK£¬ack=0£¬·¢ËÍnACK   
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDAÉèÖÃΪÊäÈë
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 
        Delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		Delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//·¢ËÍnACK
    else
        IIC_Ack(); //·¢ËÍACK   
    return receive;
}

//ÔÚAS5600Ö¸¶¨µØÖ·¶Á³öÒ»¸öÊý¾Ý
u8 AS5600_ReadOneByte(u16 ReadAddr)
{				  
	u8 temp=-1;		  	    																 
  IIC_Start();  
	IIC_Send_Byte((0X36<<1)|0x00);	   //·¢ËÍдÃüÁî
	IIC_Wait_Ack(); 
  IIC_Send_Byte(ReadAddr);   //·¢Ë͵ØÖ·
	IIC_Wait_Ack();	    
	IIC_Start();  	 	   
	IIC_Send_Byte((0X36<<1)|0x01);           //½øÈë½ÓÊÕģʽ			   
	IIC_Wait_Ack();	 
  temp=IIC_Read_Byte(0);		   
  IIC_Stop();//²úÉúÒ»¸öÍ£Ö¹Ìõ¼þ	    
	return temp;
}

//ÔÚAS5600Ö¸¶¨µØÖ··¢ËͳöÒ»¸öÊý¾Ý
void AS5600_WriteOneByte(u16 WriteAddr,u8 WriteData)
{				  	  	    																 
  IIC_Start();  
	IIC_Send_Byte((0X36<<1)|0x00);	   //·¢ËÍдÃüÁî
	IIC_Wait_Ack(); 
  IIC_Send_Byte(WriteAddr);   	//·¢Ë͵ØÖ·
	IIC_Wait_Ack();	    
	IIC_Start();  	 	   
	IIC_Send_Byte(WriteData);          //·¢ËÍÊý¾Ý		   
	IIC_Wait_Ack();	 	   
  IIC_Stop();//²úÉúÒ»¸öÍ£Ö¹Ìõ¼þ	    
	Delay_ms(10);
}

//¶ÁÈ¡Á½Î»Êý¾Ý
u16 AS5600_ReadTwoByte(u16 ReadAddr_hi,u16 ReadAddr_lo)
{
	u16 TwoByte_Data=-1;
	u8 hi_Data=0,lo_Data=0;
	hi_Data=AS5600_ReadOneByte(ReadAddr_hi);
	lo_Data=AS5600_ReadOneByte(ReadAddr_lo);
	TwoByte_Data = (hi_Data<<8)|lo_Data;
	return TwoByte_Data;
}

as5600.c

这里我们主要讲AS5600_ReadOneByte()这个函数

u8 AS5600_ReadOneByte(u16 ReadAddr)
{                  
    u8 temp=-1;                                                                                   
  IIC_Start();  
    IIC_Send_Byte((0X36<<1)|0x00);       //
    IIC_Wait_Ack(); 
  IIC_Send_Byte(ReadAddr);   //
    IIC_Wait_Ack();        
    IIC_Start();              
    IIC_Send_Byte((0X36<<1)|0x01);           //           
    IIC_Wait_Ack();     
  temp=IIC_Read_Byte(0);           
  IIC_Stop();//       
    return temp;
}

 1.先开始IIC信号

IIC_Start(); 

 2. 发送5600的写入地址

0x36是他的地址,但是要左移一位,因为最后一位是读写位,1是读,0是写

00110110<<1 |0 = 01101100 = 0x6C = 0x36<<1|0

    IIC_Send_Byte((0X36<<1)|0x00);       //发送写命令
    IIC_Wait_Ack();  //等待应答

3.发送读取的寄存器地址 0x0C或者0x0D

  IIC_Send_Byte(ReadAddr);   //发送地址
  IIC_Wait_Ack();    //等待应答

4.开始信号 

IIC_Start(); 

5.发送5600的读取地址

0x36是他的地址,但是要左移一位,因为最后一位是读写位,1是读,0是写

00110110<<1 |1 = 01101101 = 0x6D=0x36<<1|1

    IIC_Send_Byte((0X36<<1)|0x01);           //进入接收模式            
    IIC_Wait_Ack();     

6.读取数据

  temp=IIC_Read_Byte(0);           //读取数据
  IIC_Stop();        //     停止信号
  return temp;  //返回读取数据

 

 讲解完IIC的读取数据之后,我们来使用 AS5600_ReadTwoByte()函数读取两个寄存器的值

u16 AS5600_ReadTwoByte(u16 ReadAddr_hi,u16 ReadAddr_lo)
{
    u16 TwoByte_Data=-1;
    u8 hi_Data=0,lo_Data=0;
    hi_Data=AS5600_ReadOneByte(ReadAddr_hi);
    lo_Data=AS5600_ReadOneByte(ReadAddr_lo);
    TwoByte_Data = (hi_Data<<8)|lo_Data;
    return TwoByte_Data;
}

此函数是使用读取两个寄存器的值,对位的操作将两个8位的寄存器数据合并为16位数据

因为as5600是12位精度的霍尔传感器,一个8位寄存器存不下,

所以需要两个8位的寄存器来存储他的值

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "sys.h"
#include "as5600.h"



int main(void)
{
	IIC_Init();
	OLED_Init();
	OLED_ShowChar(1, 1, 'A');	
	u16 raw_num = 0;
	u16 JIAODUnum = 0;
	
	while (1)
	{

		raw_num = AS5600_ReadTwoByte(_raw_ang_hi,_raw_ang_lo);  //读取两个寄存器的值
		OLED_ShowNum(2,1,raw_num,5); //在屏幕显示
		JIAODUnum = (raw_num*360)/4096; //对寄存器值进行处理得到角度值
		OLED_ShowNum(3,1,JIAODUnum,3);//在屏幕显示

		Delay_ms(100);
	}
}

使用这个函数之后,我们就可以读取到传感器原始的角度数据

因为是12位精度的传感器,所以我们得到的数据也是12位的,即0~4095;

因为0度即360度所以我们将数据除4096再乘以360,即可得到0-359度的数据

使用合适的调试工具比如屏幕和串口,我们即可得到角度数据。

附:sys.c

#include "sys.h"

void WFI_SET(void)
{
	__ASM volatile("wfi");		  
}
//关闭所有中断
void INTX_DISABLE(void)
{		  
	__ASM volatile("cpsid i");
}
//开启所有中断
void INTX_ENABLE(void)
{
	__ASM volatile("cpsie i");		  
}
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr) 
{
    MSR MSP, r0 			//set Main Stack value
    BX r14
}

sys.h

#ifndef __SYS_H
#define __SYS_H	
#include "stm32f10x.h"	 

//0,不支持ucos
//1,支持ucos
#define SYSTEM_SUPPORT_OS		0		//定义系统文件夹是否支持UCOS
																	    
	 
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    

#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
#define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
#define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
#define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
#define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08 
 
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

//以下为汇编函数
void WFI_SET(void);		//执行WFI指令
void INTX_DISABLE(void);//关闭所有中断
void INTX_ENABLE(void);	//开启所有中断
void MSR_MSP(u32 addr);	//设置堆栈地址

#endif

物联沃分享整理
物联沃-IOTWORD物联网 » STM32使用模拟I2C读取AS5600:一步一步带你看波形图

发表评论