AS5600磁编码器(STM_IIC_HAL_DMA)
AS5600磁编码器代码参考
·
前言
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通道配置删除但要保留中断开启
更多推荐



所有评论(0)