与as5048a的SPI通信问题
本人最近在学习无刷直流电机的FOC,其中用了一个带有磁编码器as5048a作为角度反馈的电机,其中pwm接口输出是正常的,但是在SPI通信的时候出现了问题。研究了几天,没搞明白问题出在哪里,希望有大佬可以指点一下小弟。
第一张图是datasheet中截取的,第一位是偶校验,第二位是错误标志。我从as5048a收到的数始终都存在问题。第一次是发送数据地址位,第二次收数为希望的数据,但是得到的是0xC001,连偶校验都不满足,第三次发送的是判断数据有误后发送的清除错误的命令,第四次是发送的一个空指令。
第二张图是逻辑分析仪得到的数据,每100ms发一次,也就是说清除错误标志后还是这样。希望有人可以指点一下。
datasheet中还提到,发0x0000,应该收到0x0000,尝试之后没有效果。
磁编码器程序部分主要是参考的这位:
https://blog.csdn.net/nicholas_dlut/article/details/80937527
foc控制程序参考的这位:
https://www.cnblogs.com/andyfly/p/9915678.html
主函数的循环:
int main(void)
{
uint8_t tx[2]={0x00,0x00};
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
SysTick_Init();
LED_Init();//初始化LED端口
EXTI_Key_Config();
USART3_Init(500000);//与上位机通信
TIM2_Init();//系统计时,用于计算时间
SPI_Config();//采数跳变、存在问题
TIM5_Init();//用于控制电机三相信号
TIM6_Init(1000-1,84-1);//1KHz,定时器,用于多任务分频,放在所有外设初始化之后
// printf("This is a FOC program.\r\r\n");
while(1)
{
//*******************系统指示灯
if(gFlag100ms==SET)
{
LED1_TOGGLE();
PitchAngle=Read_As5048A_Reg(0x3FFF);
gFlag100ms = RESET;
}
}
SPI配置:
#include "stm32f4xx_conf.h"
#include "spi.h"
#include "delay.h"
static __IO uint32_t SPITimeout = SPIT_LONG_TIMEOUT;
void SPI_Config()
{
SPI_InitTypeDef spi;
GPIO_InitTypeDef gpio;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
// RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOH,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
//SPI的I/O初始化
gpio.GPIO_Pin = SPI_SCK_PIN|SPI_MISO_PIN|SPI_MOSI_PIN;
gpio.GPIO_Speed = GPIO_Low_Speed;
gpio.GPIO_Mode = GPIO_Mode_AF;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(SPI_GPIO_PORT, &gpio);
GPIO_PinAFConfig(SPI_GPIO_PORT,SPI_SCK_PINSOURCE,GPIO_AF_SPI2);
GPIO_PinAFConfig(SPI_GPIO_PORT,SPI_MISO_PINSOURCE,GPIO_AF_SPI2);
GPIO_PinAFConfig(SPI_GPIO_PORT,SPI_MOSI_PINSOURCE,GPIO_AF_SPI2);
//片选口
gpio.GPIO_Pin = ICGCSB_PIN;
gpio.GPIO_Mode = GPIO_Mode_OUT;
gpio.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(ICGCSB_PORT, &gpio);
GPIO_SetBits(ICGCSB_PORT,ICGCSB_PIN);
//SPI1初始化
spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//SPI通信方式:2线全双工
spi.SPI_Mode = SPI_Mode_Master;//SPI主从模式:主机模式
spi.SPI_DataSize = SPI_DataSize_16b;
spi.SPI_CPOL = SPI_CPOL_Low;//时钟极性:CPOL=0:串行同步时钟的空闲状态为低电平
spi.SPI_CPHA = SPI_CPHA_2Edge;//时钟相位:CPHA=1:串行同步时钟的第二个跳变沿(上升或下降)数据被采样
spi.SPI_NSS = SPI_NSS_Soft;//软件NSS SPI_NSS_Soft SPI_NSS_Hard
spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;//预分频系数 fSCK = 42MHz/SPI_BaudRatePrescaler
spi.SPI_FirstBit = SPI_FirstBit_MSB;//传输顺序:高位在前
spi.SPI_CRCPolynomial = 15;//CRC值计算的多项式
SPI_Init(SPI2, &spi);
SPI_Cmd(SPI2, ENABLE);//使能SPI
delay_ms(10);
}
/**
* @brief 使用SPI发送2个字节的数据
* @param Halfword:要发送的数据
* @retval 返回接收到的数据
*/
uint16_t SPI_SendHalfWord(uint16_t HalfWord)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
delay_us(1);
SPI_I2S_SendData(SPI2, HalfWord);
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
return SPI_I2S_ReceiveData(SPI2);
}
/**
* @brief 等待超时回调函数
* @param None.
* @retval None.
*/
uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
/* 等待超时后的处理,输出错误信息 */
// SPI_ERROR("SPI 等待超时!errorCode = %d",errorCode);
return 0;
}
收发校验等部分:
// Calculates even parity of 16it value, returns 1 (odd) or 0 (even)
uint8_t parity_even(uint16_t v)
{
if(v == 0) return 0;
v ^= v >> 8;
v ^= v >> 4;
v ^= v >> 2;
v ^= v >> 1;
return v & 1;
//return v ^ 1;
}
uint8_t error_flag;
uint16_t Read_As5048A_Reg(uint16_t cmd)
{
uint16_t data = 0;
uint16_t res;
uint16_t command = 0x4000;// PAR=0 R/W=R
command = command | cmd;
command |= ((uint16_t)parity_even(command)<<15);//Add a parity bit on the the MSB
SPI_SendHalfWord(command);
delay_us(2);
res = SPI_SendHalfWord(command);
delay_us(2);
error_flag = 0;
if ((res&0x4000) == 0)//判断第14位是否为0,0为正确值
{
data = (res & 0x3FFF);
error_flag = (parity_even(data) ^ (res >> 15));
LED2_OFF();
}
else//读数据错误,发送清错误命令
{
command = 0x4001;
SPI_SendHalfWord(command);
delay_us(2);
command = 0x4000;// PAR=0 R/W=R
command = command | CMD_NOP;
command |= ((uint16_t)parity_even(command)<<15);//Add a parity bit on the the MSB
SPI_SendHalfWord(command);
delay_us(2);
LED2_ON();
}
return data;
}