HAL STM32 SSI/SPI方式读取MT6701磁编码器获取角度例程
- 📍相关篇《HAL STM32 I2C方式读取MT6701磁编码器获取角度例程》
- 📌当前最新MT6701数据手册:
https://www.magntek.com.cn/upload/MT6701_Rev.1.8.pdf
- 📜SSI协议读角度,时序和数据关系
SSI方式完全可以使用3个IO配置为输入输出模式(两个配置为输出,一个配置为输入),通过SSI协议,读取电平状态,实现对数据解析。
- 📝SSI核心代码实现:(参考《STM32 HAL库 驱动 MT6701 磁编码器》)
cpp
#define Loss_of_Track 4
#define Button_Detected 3
#define Field_Weak 2
#define Field_Strong 1
#define Normal 0
/*
* SSI 方式读取信息
* mode == 0 返回角度信息,0~360 浮点数
* mode == 1 返回磁场信息
*/
double ReadDataSSI(uint8_t mode)
{
uint8_t RawData[25] = {0};
uint16_t angle = 0;
double fangle = 0;
HAL_GPIO_WritePin(SSI_CSN_GPIO_Port,SSI_CSN_Pin,GPIO_PIN_RESET); //起始信号
Delay_us(2);
HAL_GPIO_WritePin(SSI_CLK_GPIO_Port,SSI_CLK_Pin,GPIO_PIN_RESET); //跳过第一个时钟沿
Delay_us(2);
HAL_GPIO_WritePin(SSI_CLK_GPIO_Port,SSI_CLK_Pin,GPIO_PIN_SET);
Delay_us(2);
for(uint8_t i = 0; i < 25; i++) //读取数据
{
HAL_GPIO_WritePin(SSI_CLK_GPIO_Port,SSI_CLK_Pin,GPIO_PIN_RESET);
RawData[i] = HAL_GPIO_ReadPin(SSI_DI_GPIO_Port,SSI_DI_Pin);
HAL_GPIO_WritePin(SSI_CLK_GPIO_Port,SSI_CLK_Pin,GPIO_PIN_SET);
Delay_us(2);
}
HAL_GPIO_WritePin(SSI_CSN_GPIO_Port,SSI_CSN_Pin,GPIO_PIN_SET); //结束信号
for(uint8_t i = 0; i < 14; i++)
angle += RawData[i] * (2 << (13 - i));
fangle = (double)(angle * 180.0) / 16384.0; //角度信息
switch (mode)
{
case 0:
return fangle;
case 1:
{
if(RawData[14])
return Loss_of_Track;
else if(RawData[15])
return Button_Detected;
else if(RawData[16])
return Field_Weak;
else if(RawData[17])
return Field_Strong;
else
return Normal;
}
default:
return 0;
}
}
- 🌿引脚配置:
📚SSI测试工程
c
链接:https://pan.baidu.com/s/1es233q_NBo_s_fLkgbmTjw?pwd=r90i
提取码:r90i
⛳SPI方式
📗STM32 SPI参数配置:
-
🌿STM32CubeMX中SPI基本配置信息:(其中
Data Size
,这里选择配置的是8位,配置16位也是可以的,不过软件编程上就与8位的编程代码就不同,不通用)。
-
SPI串行同步时钟可以设置为不同的极性(Clock Polarity ,CPOL)与相位(Clock Phase ,CPHA)。
-
时钟的极性(CPOL)用来决定在总线空闲时,同步时钟(SCK)信号线上的电位是高电平还是低电平。
-
当时钟极性为0时(CPOL=0),SCK信号线在空闲时为低电平;当时钟极性为1时(CPOL=1),SCK信号线在空闲时为高电平;
-
时钟的相位(CPHA)用来决定何时进行信号采样。
-
当时钟相位为1时(CPHA=1),在SCK信号线的
第二个跳变沿
进行采样;这里的跳变沿究竟是上升沿还是下降沿,取决于时钟的极性。当时钟极性(CPOL)为0时,取下降沿;当时钟极性(CPOL)为1时,取上升沿;如下图:
-
🔖设置依据:根据手册SSI协议,可知:CLK空闲状态为low,决定CPOL配置为low,推荐CLK下降沿采集数据,那就配置CPHA为2,在SCK信号线的
第二个跳变沿
进行采样。
- 🌾 补充:
-
- 当时钟相位为0时(CPHA=0),在SCK信号线的
第一个跳变沿
进行采样。跳变沿同样与时钟极性有关:当时钟极性为0时,取上升沿;当时钟极性为1时,取下降沿;如下图:
- 当时钟相位为0时(CPHA=0),在SCK信号线的
- 对应:
SPI数据读取和转换实现
c
void MT6701_Read_RAW(uint8_t* pBuffer)
{
uint16_t i;
MT6701_CSN_CLR;//MT6701_CSN=0 //片选
for (i = 0; i < 4; i++)
{
pBuffer[i]=SPIx_ReadWriteByte(0xFF); //循环读入字节数据
}
MT6701_CSN_SET; //MT6701_CSN=1
}
/*!
* @brief Return position of encoder
* @return Angle value of encoder position
*/
float angleRead( void ){
float angle_f = 0.0f;
uint8_t data[3];
uint16_t angle_u16;
MT6701_Read_RAW(data);
angle_u16 = (uint16_t)(data[1] >> 2);
angle_u16 |= ((uint16_t)data[0] << 6);
angle_f = (float)angle_u16 * (360.0f/16384.0f);
return angle_f;
}
- 🎉优化处理
c
//angle_raw返回原始角度数据,angle转换后的角度值:0-360,field_status磁场强度;
void mt6701_read(uint16_t*angle_raw, float*angle, uint8_t*field_status)
{
float angle_f = 0.0f;
uint8_t status;
uint8_t data[3];
uint16_t angle_u16;
MT6701_Read_RAW(data);
angle_u16 = (uint16_t)(data[1] >> 2); //原始值
angle_u16 |= ((uint16_t)data[0] << 6);
status = (data[2] >> 6);
status |= (data[1] & 0x03) << 2;
if(angle_raw != NULL) {
*angle_raw = angle_u16;
}
if(angle != NULL) {
angle_f = (float)angle_u16 * (360.0f / 16384.0f);
*angle = angle_f;
}
if(field_status != NULL) {
*field_status = status & 0x03;
}
}
- 🔖测试效果:
📚SPI测试代码
c
链接:https://pan.baidu.com/s/1X3vUEo5mW3vEGRbXftowxw?pwd=7o6g
提取码:7o6g
📙读取24位数据并CRC校验功能测试
- 📝代码实现部分:
c
unsigned int Angle = 0;
uint32_t AngleIn24bits = 0;
uint8_t Spi_TxData[4]={0x83,0xff,0xff,0xff};///03 04 05 寄存器存角度
uint8_t Spi_pRxData[4]={0};
uint32_t ReadAngle(void)
{
//Read in Burst mode
HAL_GPIO_WritePin(MT6701_CS_GPIO_Port, MT6701_CS_Pin, GPIO_PIN_RESET);///CSN LOW
HAL_SPI_TransmitReceive(&hspi1, &Spi_TxData[0], &Spi_pRxData[0],0x03,0xffff);
HAL_GPIO_WritePin(MT6701_CS_GPIO_Port, MT6701_CS_Pin, GPIO_PIN_SET);///CSN HIGH
AngleIn24bits= (Spi_pRxData[0]<<16)|(Spi_pRxData[1]<<8)|(Spi_pRxData[2]);
// AngleIn14bits >>= 10;
return (AngleIn24bits);
}
uint8_t crc6(uint8_t *data, uint8_t length)
{
uint8_t i;
uint8_t crc = 0; // Initial value
while(length--)
{
crc ^= *data++; // crc ^= *data; data++;
for (i=6; i>0; --i)
{
if (crc & 0x20)
crc = (crc << 1) ^ 0x03;
else
crc = (crc << 1);
}
}
return crc&0x3f;
}
int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t s[3];
uint8_t crcvalue = 0;
uint32_t AngleInHex = 0;
float angle=0.0f;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SPI1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
AngleInHex = ReadAngle();
s[0]=AngleInHex>>18;
s[1]=AngleInHex>>12;
s[2]=AngleInHex>>6;
if(crc6(s, 3)==(AngleInHex&0x3f))
{
printf("\r\nReadcrc=0x%x\r\n",AngleInHex&0x3F);
printf("Cacucrc=0x%x\r\n",crc6(s, 3)&0x3F);
printf("%.2f\r\n",(AngleInHex>>10)*360/16384.0);
printf("%X\r\n",AngleInHex);
HAL_Delay(5);
printf("\n-------------------\n");
HAL_Delay(5);
}
else
{
printf("\r\nReadcrc=0x%x\r\n",AngleInHex&0x3F);
printf("Cacucrc=0x%x\r\n",crc6(s, 3)&0x3F);
printf("CRC check error!\r\n");
printf("%.2f\r\n",(AngleInHex>>10)*360/16384.0);
HAL_Delay(5);
printf("\n\r--------------------\n\r");
}
HAL_Delay(500);
}
/* USER CODE END 3 */
}
- 📄打印测试
📚读取数据并带CRC校验测试工程
c
链接:https://pan.baidu.com/s/1p-tUWT0P2aetlLwCr9X-IQ?pwd=1epn
提取码:1epn
📘SPI +DMA读取数据并带CRC校验测试工程
- 🥕基于
读取数据并带CRC校验测试工程
版本基础上调整而来的。
c
链接:https://pan.baidu.com/s/1GCT23_lJC4ocbHautazoxg?pwd=9kvi
提取码:9kvi