功能:GPIO模拟I2C读取AS5600编码器实时角度,通过printf打印到串口上,同事led状态指示灯闪烁提示程序工作正常。
一、硬件
参考资料路径:

1.编码器硬件原理图:

如图所示,SCL、SDA有接上拉电阻。
- 供电默认必须是3.3V(如果5V供电则需要断开R6)!!!
- 硬件接线:
供电:3.3V
SCL -- PB8
SDA -- PB9 - AS5600是12位的非接触式磁性旋转位置传感器
- 可以通过编程设定0度(开始位置)和最大角度(终止位置)来定义一个较小的输出范围。

- 0-360度(360度即也就是0度)对应着输出值0-4095
- 输入引脚 (DIR) 根据旋转方向选择输出极性。如果DIR 接地,那么输出值将随顺时针旋转而增加,如果DIR接至VDD, 那么输出值将随着逆时针旋转而增加(这里DIR接地,输出值随顺时针旋转而增加)。
- 从机地址为0x36 (如下图)

2.AS5600内部结构图:

二、工程实现
-
STM移植AS5600模块步骤:
1.要将AS5600文件夹拷贝到对应工程中(文件及头问价路径);
2.main.c文件中包含对应头文件;
3.如果需要修改I2C GPIO,仅修改AS5600.h即可;
4.调用IIC_AS5600_Init初始化I2C;
5.调用AS5600_ReadAngleRawValue读取0~4095范围的RAW数据;
代码
AS5600.h#ifndef __AS5600_H
#define __AS5600_H#include "system.h"
/* ####################################################################### /
/ 使用GPIO软件模拟I2C,如需改变管脚,只需改变该头文件即可 *//* IIC_SCL时钟端口、引脚定义 */
#define IIC_SCL_PORT GPIOB
#define IIC_SCL_PIN (GPIO_Pin_8)
#define IIC_SCL_PORT_RCC RCC_APB2Periph_GPIOB //为了使能端口时钟/* IIC_SDA时钟端口、引脚定义 */
#define IIC_SDA_PORT GPIOB
#define IIC_SDA_PIN (GPIO_Pin_9)
#define IIC_SDA_PORT_RCC RCC_APB2Periph_GPIOB#define IIC_SCL PBout(8) //SCL
#define IIC_SDA PBout(9) //SDA
#define READ_SDA PBin(9) //输入SDA
/* ####################################################################### *///
#define RAW_Angle_Hi 0x0C
#define RAW_Angle_Lo 0x0D
#define AS5600_Address 0x36 //从机地址
//#define IIC_SCL_1 IIC_SCL = 1
#define IIC_SCL_0 IIC_SCL = 0
#define IIC_SDA_1 IIC_SDA = 1
#define IIC_SDA_0 IIC_SDA = 0
/***************************************************************************//* 初始化I2C GPIO管脚 */
void IIC_AS5600_Init(void);/* 读取编码器数据,返回数值0-4095,对应着0~360度(0度即360度) /
/ 转换为角度 = 返回数值 * 360/4096 = 返回数值 * 0.08789 */
u16 AS5600_ReadAngleRawValue(void);#endif
AS5600.c
#include "AS5600.h"
void delay_s(u32 i);
void delay_s(u32 i)
{
while(i--);
}
//初始化IIC的IO口
void IIC_AS5600_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(IIC_SCL_PORT_RCC|IIC_SDA_PORT_RCC, ENABLE); //使能GPIOB端口时钟
GPIO_InitStructure.GPIO_Pin = IIC_SCL_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //配置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IIC_SCL_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN;
GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure);
//总线开始为空闲状态,拉高
IIC_SCL = 1;
IIC_SDA = 1;
}
/*******************************************************************************
* 函 数 名 : SDA_OUT
* 函数功能 : SDA输出配置
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //输出时配置为推挽输出
GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure);
}
/*******************************************************************************
* 函 数 名 : SDA_IN
* 函数功能 : SDA输入配置
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = IIC_SDA_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //配置为上拉输入
GPIO_Init(IIC_SDA_PORT, &GPIO_InitStructure);
}
/***************************************************************************/
void IIC_Start(void)
{
IIC_SDA_1;
IIC_SCL_1;
delay_s(20);
IIC_SDA_0;
delay_s(20);
IIC_SCL_0;
}
/***************************************************************************/
void IIC_Stop(void)
{
IIC_SCL_0;
IIC_SDA_0;
delay_s(20);
IIC_SCL_1;
IIC_SDA_1;
delay_s(20);
}
/***************************************************************************/
//1-fail,0-success
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime = 0;
SDA_IN();
IIC_SDA_1;
IIC_SCL_1;
delay_s(10);
while(READ_SDA!=0)
{
if(++ucErrTime>250)
{
SDA_OUT();
IIC_Stop();
return 1;
}
}
SDA_OUT();
IIC_SCL_0;
return 0;
}
/***************************************************************************/
void IIC_Ack(void)
{
IIC_SCL_0;
IIC_SDA_0;
delay_s(20);
IIC_SCL_1;
delay_s(20);
IIC_SCL_0;
}
/***************************************************************************/
void IIC_NAck(void)
{
IIC_SCL_0;
IIC_SDA_1;
delay_s(20);
IIC_SCL_1;
delay_s(20);
IIC_SCL_0;
}
/***************************************************************************/
void IIC_Send_Byte(u8 txd)
{
u32 i;
IIC_SCL_0;
for(i = 0; i < 8; i++)
{
if((txd&0x80)!=0)
IIC_SDA_1;
else
IIC_SDA_0;
txd<<=1;
delay_s(20);
IIC_SCL_1;
delay_s(20);
IIC_SCL_0;
delay_s(20);
}
}
/***************************************************************************/
u8 IIC_Read_Byte(u8 ack)
{
u8 i, rcv = 0;
SDA_IN();
for(i = 0; i < 8; i++)
{
IIC_SCL_0;
delay_s(20);
IIC_SCL_1;
rcv<<=1;
if(READ_SDA!=0)
rcv++;
delay_s(10);
}
SDA_OUT();
if(!ack)
IIC_NAck();
else
IIC_Ack();
return rcv;
}
/***************************************************************************/
u8 AS5600_ReadOneByte(u8 addr)
{
u8 temp;
IIC_Start();
IIC_Send_Byte(AS5600_Address<<1);
IIC_Wait_Ack();
IIC_Send_Byte(addr);
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte((AS5600_Address<<1)+1);
IIC_Wait_Ack();
temp = IIC_Read_Byte(0);
IIC_Stop();
return temp;
}
/***************************************************************************/
u16 AS5600_ReadAngleRawValue(void)
{
u8 dh, dl;
IIC_Start();
IIC_Send_Byte(AS5600_Address<<1);
IIC_Wait_Ack();
IIC_Send_Byte(RAW_Angle_Hi);
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte((AS5600_Address<<1)+1);
IIC_Wait_Ack();
dh = IIC_Read_Byte(1); //1-ack for next byte
dl = IIC_Read_Byte(0); //0-end trans
IIC_Stop();
return ((dh<<8) + dl);
}
/***************************************************************************/
main.c
#include "system.h" //已经包含了"stm32f10x.h" 以及定义了位带操作的宏,后续只包含该头文件就可以了
#include "SysTick.h" //使用delay_ms和delay_us
#include "led.h" //Led_Init()对8个LED GPIO初始化, 这里不需要,有在TIM3_CH1_PWM_Init完成了对GPIO的初始化
#include "usart.h" //在该例程中需要使用printf函数
#include "AS5600.h"
int main()
{
u8 i = 0;
u8 j = 0;
u16 raw_value = 0;
float angle_value = 0.0;
SysTick_Init(72); //72为SYSCLK delay_ms延时函数需要使用
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断分组2:2 在使用中断回调函数时都需要调用, 这里虽然没有使用中断,但还是留着
Led_Init();
USART1_Init(9600);
IIC_AS5600_Init();
printf("AS5600编码器读取: \r\n");
while(1)
{
i++;
if(i%20 == 0)
{
led1 = !led1; //200ms闪烁一次
j++;
if(j%3 == 0) //600ms读取一次编码器值
{
raw_value = AS5600_ReadAngleRawValue();
angle_value = raw_value * 0.08789;
printf("Value(0-4095):%d - Angle(0-360):%.4f\r\n", raw_value, angle_value);
}
}
delay_ms(10);
}
}
三、演示

