前言

as5600磁编码器是开始学习FOC时非常合适的一款传感器,主要是网上资料多适合学生党上手做实验。数据手册传送门:Ams-Osram-AS5600-DS000365_EN.pdf

一. IIC通讯

i2c原理建议花几分钟看看:4分钟看懂!I2C通讯协议 最简单的总线通讯!_哔哩哔哩_bilibili

下面是芯片的电气和时序要求

二. 硬件电路

这里引用网上大佬的原理图为例,使用的是3.3V的供电方案

三. CubeMX配置

使用的型号是F103C8T6

0.Debug配置

1.IIC配置

2.DMA配置

3.中断配置

四. 代码

有一些参数重复或没用上但不影响功能,硬件iic使用前AS5600_Init()一下(好像不init也没关系,毕竟index一开始的值默认也是0x00)但软件必须要IIC_Init()再调用Get_Angle2() 或 Get_Angle() 。软硬件均亲测可用

 4.1 硬件IIC+DMA

iic.h

#ifndef _IIC__H_
#define _IIC__H_
#include "stdint.h"
#include <stdio.h>
#include <stdbool.h>

typedef struct
{
	uint8_t index;
	uint8_t read_buffer[2];//角度高低字节
	uint8_t average_count;
	uint8_t H_Byte;//角度高字节
	uint8_t L_Byte;//角度低字节
	uint8_t RAW_Angle_temp[10];//滤波
	float RAW_Angle;	//原始角度
	float Angle_temp[10];//滤波
	float Angle;			//真实角度
	uint16_t delay_time;//软件IIC延时时间
	uint8_t Slave_addr;	//从机地址
	uint8_t Pointer_addr;	//寄存器地址
	uint8_t Ack_flag;
}Magnetic_Encoder;

//================硬件iic
#define AS5600_DEVICE_ADDRESS   0x36
#define AS5600_angle_Hbyte  0x0C
#define AS5600_angle_Lbyte  0x0D

void Get_Angle2(Magnetic_Encoder *Encoder);
#endif

iic.c

#include "iic.h"
#include "i2c.h"
#include "main.h"

Magnetic_Encoder AS5600;
void AS5600_Init()
{
	AS5600.Slave_addr=0x36;
	AS5600.index=0x00;
}
uint8_t write_buffer[] = {AS5600_angle_Hbyte};
//================硬件iic
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if (hi2c->Instance == I2C1)
		{
			HAL_I2C_Master_Receive_DMA(&hi2c1, AS5600_DEVICE_ADDRESS<<1 , AS5600.read_buffer, sizeof(AS5600.read_buffer));
		}
}
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if (hi2c->Instance == I2C1)
		{
			AS5600.index=0x04;
//			AS5600.Angle=(float)((((AS5600.read_buffer[0]<<8)|AS5600.read_buffer[1])*360)/4096);
		}
}
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)
{
    uint32_t error = HAL_I2C_GetError(hi2c);
//		AS5600.index=0x00;
    // 处理I2C错误(如总线错误、ACK失败等)
}
void Get_Angle2(Magnetic_Encoder *Encoder)
{	
	switch(Encoder->index)
	{
		case 0x00:
			HAL_I2C_Master_Transmit_DMA(&hi2c1, AS5600_DEVICE_ADDRESS<<1,write_buffer, 1);
//			HAL_I2C_Master_Transmit_IT(&hi2c1, AS5600_DEVICE_ADDRESS,write_buffer, 1);
//			HAL_I2C_Mem_Write_IT(&hi2c1, AS5600_DEVICE_ADDRESS<<1,0x0C,I2C_MEMADD_SIZE_8BIT,0x00,1);
			Encoder->index=0x01;
		break;
		case 0x01:

		break;
		case 0x04:
			Encoder->Angle=(float)((((Encoder->read_buffer[0]<<8)|Encoder->read_buffer[1])*360)/4096);
			Encoder->index=0x00;
		break;
		default:
			Encoder->index=0x00;
    break;
	}
}

4.2 软件IIC

iic.h

#ifndef _IIC__H_
#define _IIC__H_
#include "main.h"

typedef struct
{
	uint8_t index;
	uint8_t read_buffer[2];//角度高低字节
	uint8_t average_count;
	uint8_t H_Byte;//角度高字节
	uint8_t L_Byte;//角度低字节
	uint8_t RAW_Angle_temp[10];//滤波
	float RAW_Angle;	//原始角度
	float Angle_temp[10];//滤波
	float Angle;			//真实角度
	uint16_t delay_time;//软件IIC延时时间
	uint8_t Slave_addr;	//从机地址
	uint8_t Pointer_addr;	//寄存器地址
	uint8_t Ack_flag;
}Magnetic_Encoder;

//================软件iic
#define SCL_PIN GPIO_PIN_6
#define SDA_PIN GPIO_PIN_7

#define SCL_PORT GPIOB
#define SDA_PORT GPIOB
#define SCL_PORT_Num 1 //0:GPIOA 1:GPIOB 2:GPIOC 3:GPIOD 4:GPIOE 5:GPIOF 6:GPIOG
#define SDA_PORT_Num 1 //0:GPIOA 1:GPIOB 2:GPIOC 3:GPIOD 4:GPIOE 5:GPIOF 6:GPIOG

#define IIC_SCL_H   HAL_GPIO_WritePin(SCL_PORT,SCL_PIN,GPIO_PIN_SET) //SCL输出高电平
#define IIC_SDA_H   HAL_GPIO_WritePin(SDA_PORT,SDA_PIN,GPIO_PIN_SET) //SDA输出高电平
#define IIC_SCL_L   HAL_GPIO_WritePin(SCL_PORT,SCL_PIN,GPIO_PIN_RESET) //SCL输出低电平
#define IIC_SDA_L   HAL_GPIO_WritePin(SDA_PORT,SDA_PIN,GPIO_PIN_RESET) //SDA输出低电平

#define IIC_SDA_In	HAL_GPIO_ReadPin (SDA_PORT,SDA_PIN) //输入SDA状态 


void IIC_Init(void);
void SDA_IN(void);
void SDA_OUT(void);
void SCL_OUT(void);
void IIC_Start(void);
void IIC_Stop(void);
uint8_t IIC_Wait_Ack(void);
void IIC_Ack(void);
void IIC_NAck(void);
void Send_Byte(uint8_t tx);
uint8_t Read_Byte(void);
void Get_Angle(Magnetic_Encoder *Encoder);
void delay_us(uint32_t us);

void Sim_I2C1_Delay(uint32_t delay);
#define Sim_I2C1_DELAY 		Sim_I2C1_Delay(100000)
#define Sim_I2C1_NOP		  Sim_I2C1_Delay(400)  //25

#endif

iic.c

//================软件iic
void Get_Angle(Magnetic_Encoder *Encoder)
{
	//--------读角度低字节
	IIC_Start();
	Send_Byte(AS5600.Slave_addr<<1 | 0x00);//写从机地址
	Encoder->Ack_flag = IIC_Wait_Ack();
	Send_Byte(0x0D);//写寄存器
	Encoder->Ack_flag = IIC_Wait_Ack();
	
	IIC_Start();
	Send_Byte(AS5600.Slave_addr<<1 | 0x01);//读
	Encoder->Ack_flag = IIC_Wait_Ack();
	Encoder->L_Byte = Read_Byte();
	IIC_Stop();
	
	delay_us(1);;
	//--------读角度高字节
	IIC_Start();
	Send_Byte(AS5600.Slave_addr<<1 | 0x00);//写从机地址
	Encoder->Ack_flag = IIC_Wait_Ack();
	Send_Byte(0x0C);//写寄存器
	Encoder->Ack_flag = IIC_Wait_Ack();
	
	IIC_Start();
	Send_Byte(AS5600.Slave_addr<<1 | 0x01);//读
	Encoder->Ack_flag = IIC_Wait_Ack();
	Encoder->H_Byte = Read_Byte();
	IIC_Stop();
	
//	IIC_Wait_Ack();
//	Encoder->H_Byte=Read_Byte();
//	IIC_Wait_Ack();

	Encoder->RAW_Angle_temp[Encoder->average_count]=((Encoder->H_Byte<<8)|Encoder->L_Byte);
	
	Encoder->Angle=(float)((((Encoder->H_Byte<<8)|Encoder->L_Byte)*360)/4096);
//	if(Encoder->average_count <10)
//		Encoder->average_count++;
//	else
//	{
//		Encoder->RAW_Angle=0;
//		for(uint8_t i=0;i<10;i++)
//		{
//			Encoder->RAW_Angle+=Encoder->RAW_Angle_temp[i];
//		}
//		Encoder->Angle=(float)(((Encoder->RAW_Angle/10)*360)/4096);
//		Encoder->average_count=0;
////		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
//	}
}

void IIC_Init(void)
{
	SCL_OUT();
	SDA_OUT();
	IIC_SCL_H;
	IIC_SDA_H;
	AS5600_Init();
}
void SDA_IN(void)
{
	#if SDA_PORT_Num == 0
	__HAL_RCC_GPIOA_CLK_ENABLE();//GPIO使能
	#elif SDA_PORT_Num == 1
	__HAL_RCC_GPIOB_CLK_ENABLE();
	#elif SDA_PORT_Num == 2
	__HAL_RCC_GPIOC_CLK_ENABLE();
	#elif SDA_PORT_Num == 3
	__HAL_RCC_GPIOD_CLK_ENABLE();
	#elif SDA_PORT_Num == 4
	__HAL_RCC_GPIOE_CLK_ENABLE();
	#elif SDA_PORT_Num == 5
	__HAL_RCC_GPIOF_CLK_ENABLE();
	#elif SDA_PORT_Num == 6
	__HAL_RCC_GPIOG_CLK_ENABLE();
	#endif
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	/*Configure GPIO pins : PB6 PB7 */
  GPIO_InitStruct.Pin = SDA_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(SDA_PORT, &GPIO_InitStruct);
}
void SDA_OUT(void)
{
	#if SDA_PORT_Num == 0
	__HAL_RCC_GPIOA_CLK_ENABLE();//GPIO使能
	#elif SDA_PORT_Num == 1
	__HAL_RCC_GPIOB_CLK_ENABLE();
	#elif SDA_PORT_Num == 2
	__HAL_RCC_GPIOC_CLK_ENABLE();
	#elif SDA_PORT_Num == 3
	__HAL_RCC_GPIOD_CLK_ENABLE();
	#elif SDA_PORT_Num == 4
	__HAL_RCC_GPIOE_CLK_ENABLE();
	#elif SDA_PORT_Num == 5
	__HAL_RCC_GPIOF_CLK_ENABLE();
	#elif SDA_PORT_Num == 6
	__HAL_R
	#endif
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	/*Configure GPIO pins : PB6 PB7 */
  GPIO_InitStruct.Pin = SDA_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(SDA_PORT, &GPIO_InitStruct);
}
void SCL_OUT(void)
{
	#if SCL_PORT_Num == 0
	__HAL_RCC_GPIOA_CLK_ENABLE();//GPIO使能
	#elif SCL_PORT_Num == 1
	__HAL_RCC_GPIOB_CLK_ENABLE();
	#elif SCL_PORT_Num == 2
	__HAL_RCC_GPIOC_CLK_ENABLE();
	#elif SCL_PORT_Num == 3
	__HAL_RCC_GPIOD_CLK_ENABLE();
	#elif SCL_PORT_Num == 4
	__HAL_RCC_GPIOE_CLK_ENABLE();
	#elif SCL_PORT_Num == 5
	__HAL_RCC_GPIOF_CLK_ENABLE();
	#elif SCL_PORT_Num == 6
	__HAL_R
	#endif
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	/*Configure GPIO pins : PB6 PB7 */
  GPIO_InitStruct.Pin = SCL_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(SCL_PORT, &GPIO_InitStruct);
}
void IIC_Start(void)
{
	SDA_OUT();
	IIC_SDA_H;
	IIC_SCL_H;
	//
	delay_us(1);;
	IIC_SDA_L;
	//
	delay_us(1);;
	IIC_SCL_L;
}
void IIC_Stop(void)
{
	SCL_OUT();
	IIC_SCL_L;
	IIC_SDA_L;
	//
	delay_us(1);;
	IIC_SCL_H;
	//
	delay_us(1);;
	IIC_SDA_H;
	//
	delay_us(1);;
}
uint8_t IIC_Wait_Ack(void)
{
	uint8_t Wait_Time=0;
	SDA_IN();
	//
	delay_us(1);;
	IIC_SCL_H;
	//
	delay_us(1);;
	while(IIC_SDA_In)//从机应答把SDA拉低
	{
		Wait_Time++;
		if(Wait_Time>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL_L;
	return 0;
}
void IIC_Ack(void)
{
	IIC_SCL_L;
	SDA_OUT();
	IIC_SDA_L;
	//
	delay_us(1);;
	IIC_SCL_H;
	//
	delay_us(1);;
	IIC_SCL_L;
}
void IIC_NAck(void)
{
	IIC_SCL_L;
	SDA_OUT();
	IIC_SDA_H;
	//
	delay_us(1);;
	IIC_SCL_H;
	//
	delay_us(1);;
	IIC_SCL_L;
}
void Send_Byte(uint8_t tx)
{
	SDA_OUT();
	IIC_SCL_L;
	for(uint8_t i=0;i<8;i++)
	{
		if((tx<<i)&0x80)
			IIC_SDA_H;
		else
			IIC_SDA_L;
		//
		delay_us(1);;
		IIC_SCL_H;
		//
		delay_us(1);;
		IIC_SCL_L;
	}
}
uint8_t Read_Byte()
{
	uint8_t data=0;
	SDA_IN();
	for(uint8_t i=0;i<8;i++)
	{
		IIC_SCL_L;
		//
		delay_us(1);;
		IIC_SCL_H;
		//
		delay_us(1);;
		if(IIC_SDA_In)
			data |= (1 << (7 - i));
		//
		delay_us(1);;
	}
	return data;
}
void delay_us(uint32_t us)
{
    uint32_t delay = (HAL_RCC_GetHCLKFreq() / 4000000) * us;  // 根据系统时钟频率计算循环次数
    while (delay--) {
        __NOP();  // 空操作指令,避免编译器优化
    }
}
void Sim_I2C1_Delay(uint32_t delay)
{
	while(--delay);	//dly=100: 8.75us; dly=100: 85.58 us (SYSCLK=72MHz)
}

五. 补充

前人栽树后人乘凉,希望这篇文章对大家有所帮助,后续遇到相关问题也会补充说明

2025/5/20 【遇到问题】IIC+DMA的方法偶尔不成功无法进入回调函数导致无法读取角度数据 ,原因未知

【解决方法】改为IIC+中断形式读取角度数据

【具体操作】1. HAL_I2C_Master_Transmit_DMA 更改为 HAL_I2C_Master_Transmit_IT       HAL_I2C_Master_Receive_DMA 更改为 HAL_I2C_Master_Receive_IT 传输参数保持不变仅更改收发函数  2.在CubeMX中将IIC的DMA通道配置删除但要保留中断开启

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐