IIC读取AS5600磁编码器并计算相对角度与圈数代码移植

芯片F103CBT6,使用cubeide+hal库,驱动部分源码移植于:

(22条消息) STM32使用模拟I2C读取AS5600(深入讲解:带波形图)_yizeni的博客-CSDN博客_as5600使用方法

使用前需要重定向printf,方便串口调试;此外,sys库是由正点原子那个由m3内核直接写寄存器的位带操作库,无法用于其他cortex-m内核的mcu,使用f4 f7等型号时需要自行修改模拟iic的底层代码(sda scl拉高拉低等)。

对函数以及全局变量进行了适当的封装与static声明,能计算上电后相对初始位置的转角与圈数,注释的utf8跟gbk互转的时候部分搞出乱码了,将就看看。

AS5600.h:

/*
 * as5600.h
 *
 *  Created on: Jan 19, 2023
 *      Author: Administrator
 */

#ifndef _AS5600_H
#define _AS5600_H

#include "sys.h"
#include "main.h"

//角度相关的全项目全局变量
extern short relativeAngle;//相对旋转角度
extern short totalAngle;//总旋转角度
extern short numberofTurns;//旋转圈数

//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(3) //SCL输出
#define IIC_SDA    PBout(4) //SDA输出
#define READ_SDA   PBin(4)  //SDA输入

//AS5600的高低地址
#define	_raw_ang_hi 0x0c
#define	_raw_ang_lo 0x0d

/*
 AS5600驱动的静态函数封装
static u8 AS5600_ReadOneByte(u16 ReadAddr);
static void AS5600_WriteOneByte(u16 WriteAddr,u8 WriteData);
static u16 AS5600_ReadTwoByte(u16 ReadAddr_hi,u16 ReadAddr_lo);
*/

void AS5600_Test(void);//测试,每次依次输出寄存器读值,绝对角度值,相对角度值,圈数

void RelativeAngleCorrect(short startAngle, short realAngle);//相对角度正值化
void checkQuadrant(void);//用象限来计算圈数,顺时针转为正


#endif

AS5600.c

/*
 * as5600.c
 *
 *  Created on: Jan 19, 2023
 *      Author: Administrator
 */


#include "as5600.h"
#include "Delay.h"
#include "stm32f1xx_hal.h"
#include <stdio.h>

/*  iic底层相关静态函数*/

//IO方向设置
static void SDA_IN(void);
static void SDA_OUT(void);

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

//as5600驱动
static u8 AS5600_ReadOneByte(u16 ReadAddr);
static void AS5600_WriteOneByte(u16 WriteAddr,u8 WriteData);
static u16 AS5600_ReadTwoByte(u16 ReadAddr_hi,u16 ReadAddr_lo);

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

//角度相关全局变量
short relativeAngle = 0;//相对旋转角度
short totalAngle = 0;//总旋转角度
short numberofTurns = 0;//旋转圈数

static short previousquadrantNumber = 0;//上次象限
static short quadrantNumber = 0;//当前象限

static void SDA_IN(void){
	  GPIO_InitTypeDef GPIO_InitStruct = {0};
	  GPIO_InitStruct.Pin = SDA_Pin;
	  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
	  GPIO_InitStruct.Pull = GPIO_NOPULL;
	  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	  HAL_GPIO_Init(SDA_GPIO_Port, &GPIO_InitStruct);
}

static void SDA_OUT(void){
	  GPIO_InitTypeDef GPIO_InitStruct = {0};
	  GPIO_InitStruct.Pin = SDA_Pin;
	  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
	  GPIO_InitStruct.Pull = GPIO_NOPULL;
	  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	  HAL_GPIO_Init(SDA_GPIO_Port, &GPIO_InitStruct);
}

//³õʼ»¯IIC
static void IIC_Init(void)
{
	  GPIO_InitTypeDef GPIO_InitStruct = {0};

	  /* GPIO Ports Clock Enable */
	  __HAL_RCC_GPIOD_CLK_ENABLE();
	  __HAL_RCC_GPIOB_CLK_ENABLE();
	  __HAL_RCC_GPIOA_CLK_ENABLE();
	  /*Configure GPIO pin : SCL_Pin */
	  GPIO_InitStruct.Pin = SCL_Pin;
	  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	  GPIO_InitStruct.Pull = GPIO_NOPULL;
	  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	  HAL_GPIO_Init(SCL_GPIO_Port, &GPIO_InitStruct);

	  /*Configure GPIO pin : SDA_Pin */
	  GPIO_InitStruct.Pin = SDA_Pin;
	  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
	  GPIO_InitStruct.Pull = GPIO_NOPULL;
	  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	  HAL_GPIO_Init(SDA_GPIO_Port, &GPIO_InitStruct);
}
//²úÉúIICÆðʼÐźÅ
static 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Í£Ö¹ÐźÅ
static 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£¬½ÓÊÕÓ¦´ð³É¹¦
static 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Ó¦´ð
static 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Ó¦´ð
static 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£¬ÎÞÓ¦´ð
static 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
static 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Ö¸¶¨µØÖ·¶Á³öÒ»¸öÊý¾Ý
static 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Ö¸¶¨µØÖ··¢ËͳöÒ»¸öÊý¾Ý
static 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();//²úÉúÒ»¸öÍ£Ö¹Ìõ¼þ
	HAL_Delay(10);
}

//¶ÁÈ¡Á½Î»Êý¾Ý
static 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;
}

void AS5600_Test(void){
	//配置顺时针为正

	IIC_Init();
	short raw_num = 0;//寄存器读值
	short real_start_angle = 0;//初始绝对角度
	short real_angle = 0;//读取的绝对角度

	//获取初始角
	raw_num = AS5600_ReadTwoByte(_raw_ang_hi,_raw_ang_lo);
	real_start_angle = (raw_num*360)/4096; //对寄存器值进行处理得到角度值

	while (1)
	{
		raw_num = AS5600_ReadTwoByte(_raw_ang_hi,_raw_ang_lo);  //读取两个寄存器的值
		//printf("adr num: %d\r\n",raw_num);
		real_angle = (raw_num*360)/4096; //对寄存器值进行处理得到角度值
		RelativeAngleCorrect(real_start_angle, real_angle);//获得相对角度
		checkQuadrant();
		printf("\r\n");
		printf("寄存器读值: %d\r\n",raw_num);
		printf("绝对角度: %d\r\n",real_angle);
		printf("相对角度: %d\r\n",relativeAngle);
		printf("圈数: %d\r\n",numberofTurns);
		printf("\r\n");
		LED_TURN;
		HAL_Delay(100);
	}
}

void RelativeAngleCorrect(short startAngle, short realAngle){
	//相对角度获取,顺时针为正
	  relativeAngle = realAngle - startAngle; //没变动则为0

	  if(relativeAngle < 0) //把相对角度转换为0-360度表示
	  {
		  relativeAngle = relativeAngle + 360; //
	  }

	  //把0-360角度转换为-180到+180表示
	  if(relativeAngle > 180){
		  relativeAngle = relativeAngle - 360;
	  }
}

void checkQuadrant(void)
{
	//用象限来计算圈数,顺时针转为正
  /*
  //Quadrants:
  4  |  1
  ---|---
  3  |  2
  */
  //Quadrant 1
  if(relativeAngle > 0 && relativeAngle <=90)
  {
    quadrantNumber = 1;
  }

  //Quadrant 2
  if(relativeAngle > 90 && relativeAngle <=180)
  {
    quadrantNumber = 2;
  }

  //Quadrant 3
  if(relativeAngle > -180 && relativeAngle <=-90)
  {
    quadrantNumber = 3;
  }

  //Quadrant 4
  if(relativeAngle > -90 && relativeAngle <0)
  {
    quadrantNumber = 4;
  }

  if(quadrantNumber != previousquadrantNumber) //如果象限改变
  {
    if(quadrantNumber == 1 && previousquadrantNumber == 4)
    {
      numberofTurns++; // 现在象限为1而上次为4,则顺时针旋转过0一次,圈数加1
    }

    if(quadrantNumber == 4 && previousquadrantNumber == 1)
    {
      numberofTurns--; // 现在象限为4而上次为1,则逆时针旋转过0一次,圈数减1
    }

    previousquadrantNumber = quadrantNumber;  //更新旧象限

  }

  totalAngle = (numberofTurns*360) + relativeAngle; //number of turns (+/-) plus the actual angle within the 0-360 range

}

附件:一个hal库的systick延时库

/*
 * Delay.c
 *
 *  Created on: Jan 19, 2023
 *      Author: Administrator
 */

/* --------------------------------------------------------- */
// 文件名称:delay.c
// 功能描述: SysTick延时函数源文件

/* --------------------------------------------------------- */

#include "Delay.h"
#include "main.h"
#include "stm32f1xx_hal.h"

float usDelayBase;
void PY_usDelayTest(void)
{
  uint32_t firstms, secondms;
  uint32_t counter = 0;

  firstms = HAL_GetTick()+1;
  secondms = firstms+1;

  while(uwTick!=firstms) ;

  while(uwTick!=secondms) counter++;

  usDelayBase = ((float)counter)/1000;
}

void Delay_us_t(uint32_t Delay)
{
  uint32_t delayReg;
  uint32_t usNum = (uint32_t)(Delay*usDelayBase);

  delayReg = 0;
  while(delayReg!=usNum) delayReg++;
}

void PY_usDelayOptimize(void)
{
  uint32_t firstms, secondms;
  float coe = 1.0;

  firstms = HAL_GetTick();
  Delay_us_t(1000000) ;
  secondms = HAL_GetTick();

  coe = ((float)1000)/(secondms-firstms);
  usDelayBase = coe*usDelayBase;
}


void Delay_us(uint32_t Delay)
{
  uint32_t delayReg;

  uint32_t msNum = Delay/1000;
  uint32_t usNum = (uint32_t)((Delay%1000)*usDelayBase);

  if(msNum>0) HAL_Delay(msNum);

  delayReg = 0;
  while(delayReg!=usNum) delayReg++;
}

  • 10
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值