在spi通信中,需要设置cpol极性和cpha相位,保持master和slave两端时钟一致,相互匹配才能正常通信。主要原因是spi没有握手信号不可靠传输。
名词解释
CKPOL (Clock Polarity) = CPOL = POL = Polarity = (时钟)极性
CKPHA (Clock Phase) = CPHA = PHA = Phase = (时钟)相位
SCK=SCLK=SPI的时钟
Edge=边沿,即时钟电平变化的时刻,即上升沿(rising edge)或者下降沿(falling edge)
对于一个时钟周期内,有两个edge,分别称为:
(1)Leading edge=前一个边沿=第一个边沿,对于开始电压是1,那么就是1变成0的时候,对于开始电压是0,那么就是0变成1的时候。
(2)Trailing edge=后一个边沿=第二个边沿,对于开始电压是1,那么就是0变成1的时候(即在第一次1变成0之后,才可能有后面的0变成1),对于开始电压是0,那么就是1变成0的时候。
CPOL和CPHA,分别都可以是0或时1,对应的四种组合就是:
CPOL=0,时钟空闲低电平,当SCLK有效的时候,就是高电平,就是所谓的active-high;
CPOL=1,时钟空闲高电平,当SCLK有效的时候,就是低电平,就是所谓的active-low;
CPHA=0,表示第一个边沿:
对于CPOL=0,idle时候的是低电平,第一个边沿就是从低变到高,所以是上升沿;
对于CPOL=1,idle时候的是高电平,第一个边沿就是从高变到低,所以是下降沿;
CPHA=1,表示第二个边沿:
对于CPOL=0,idle时候的是低电平,第二个边沿就是从高变到低,所以是下降沿;
对于CPOL=1,idle时候的是高电平,第一个边沿就是从低变到高,所以是上升沿;
波形图
参考手册《IMX8QXPAEC.pdf》4.10.1 LPSPI timing parameters
默认设置CPOL=0,CPHA=0;时钟空闲低电平,第一个边沿采样。
代码设置
Cortex-M4代码设置:
/*! @brief LPSPI clock polarity configuration.*/
typedef enum _lpspi_clock_polarity
{
kLPSPI_ClockPolarityActiveHigh = 0U, /*!< CPOL=0. Active-high LPSPI clock (idles low)*/
kLPSPI_ClockPolarityActiveLow = 1U /*!< CPOL=1. Active-low LPSPI clock (idles high)*/
} lpspi_clock_polarity_t;
/*! @brief LPSPI clock phase configuration.*/
typedef enum _lpspi_clock_phase
{
kLPSPI_ClockPhaseFirstEdge = 0U, /*!< CPHA=0. Data is captured on the leading edge of the SCK and changed on the
following edge.*/
kLPSPI_ClockPhaseSecondEdge = 1U /*!< CPHA=1. Data is changed on the leading edge of the SCK and captured on the
following edge.*/
} lpspi_clock_phase_t;
/*! @brief LPSPI master configuration structure.*/
typedef struct _lpspi_master_config
{
lpspi_clock_polarity_t cpol; /*!< Clock polarity. */
lpspi_clock_phase_t cpha; /*!< Clock phase. */
} lpspi_master_config_t;
/*! @brief LPSPI slave configuration structure.*/
typedef struct _lpspi_slave_config
{
lpspi_clock_polarity_t cpol; /*!< Clock polarity. */
lpspi_clock_phase_t cpha; /*!< Clock phase. */
} lpspi_slave_config_t;
Transmit Command Register (TCR)
在A35核中,dts中没有设置项,代码spi-fsl-lpspi.c中也没有设置,默认都是0模式。
#define TCR_CPOL BIT(31)
#define TCR_CPHA BIT(30)
用户程序使用SPI_IOC_RD_MODE设置mode无效,SPI驱动中没有设置的代码:
unsigned char mode = 0;
ret = ioctl(fd, SPI_IOC_RD_MODE,&mode);
if(ret < 0)
{
perror("ioctl SPI_IOC_RD_MODE");
return -1;
}
驱动中手动设置CPHA模式:temp |= TCR_CPHA;
static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi)
{
u32 temp = 0;
if (!fsl_lpspi->is_slave) {
temp |= fsl_lpspi->config.bpw - 1;
temp |= fsl_lpspi->config.prescale << 27;
temp |= (fsl_lpspi->config.mode & 0x3) << 30;
temp |= (fsl_lpspi->config.chip_select & 0x3) << 24;
/*
* Set TCR_CONT will keep SS asserted after current transfer.
* For the first transfer, clear TCR_CONTC to assert SS.
* For subsequent transfer, set TCR_CONTC to keep SS asserted.
*/
if (!fsl_lpspi->usedma) {
temp |= TCR_CONT;
if (fsl_lpspi->is_first_byte)
temp &= ~TCR_CONTC;
else
temp |= TCR_CONTC;
}
} else {
temp |= fsl_lpspi->config.bpw - 1;
temp |= (fsl_lpspi->config.mode & 0x3) << 30;
temp |= TCR_CPHA;
}
writel(temp, fsl_lpspi->base + IMX7ULP_TCR);
dev_dbg(fsl_lpspi->dev, "TCR=0x%x\n", temp);
}
根据文章stm32 硬件SPI接收 数据的干扰、丢包的一些感想_奥利奥冰茶的博客-CSDN博客_spi丢包
SPI丢包大概率是时钟上升沿采样容易收到毛刺波形的干扰,使用CPHA=1模式,在下降沿采数据比较干净不容易收到干扰,测试效果如下:
1MHz:
【T=100us】【cpha=0】
SPI TEST OVER:total 199970, 0, 17, packages,time:26063671
SPI TEST OVER:total 199974, 0, 15, packages,time:25902166
SPI TEST OVER:total 199979, 0, 11, packages,time:25812487
SPI TEST OVER:total 199959, 0, 22, packages,time:25739095
【T=100us】【cpha=1】
SPI TEST OVER total:199994, lost:0, error:3, time:40820 ms
SPI TEST OVER total:199993, lost:0, error:5, time:40819 ms
SPI TEST OVER total:199994, lost:0, error:3, time:40819 ms
测试代码:
masterConfig.cpha = kLPSPI_ClockPhaseFirstEdge;
masterConfig.cpha = kLPSPI_ClockPhaseSecondEdge;
masterConfig.baudRate = 1000000;
static void hello_task(void *pvParameters)
{
uint8_t *tmp=NULL;
uint32_t cnt = 0;
uint32_t currentTime;
char ch;
tmp = (uint8_t *)(&cnt);
send_buffer[0] = 'g';
send_buffer[1] = 'e';
send_buffer[2] = 'n';
send_buffer[3] = 'v';
send_buffer[4] = 0;
send_buffer[5] = 0;
send_buffer[6] = 0;
send_buffer[7] = 0;
spi_data.dataSize = 8;
PRINTF("\r\n#################### spi3 master test ####################\n\r\n");
PRINTF(" Build Time: %s--%s \n\r\n", __DATE__, __TIME__);
PRINTF("##########################################################\r\n");
PRINTF("please input enter start send data:\r\n");
ch = GETCHAR();
PRINTF("ch=%c\r\n",ch);
for (;;)
{
cnt++;
send_buffer[4] = tmp[0];
send_buffer[5] = tmp[1];
send_buffer[6] = tmp[2];
send_buffer[7] = tmp[3];
if (kStatus_Success != LPSPI_RTOS_TransferBlocking(&handle, &spi_data))//自构建阻塞,可以发送数据
{
PRINTF("Command Transmission fails.\r\n");
vTaskSuspend(NULL);
}
//延时微秒
SDK_DelayAtLeastUs(100, SystemCoreClock);
if (cnt == 1){
currentTime = OSA_TimeGetMsec();
PRINTF("SPI TEST START:%d\r\n",currentTime);
SDK_DelayAtLeastUs(1000, SystemCoreClock);
}
if (cnt == 200000){
currentTime = OSA_TimeGetMsec();
PRINTF("SPI TEST OVER: %d\r\n",currentTime);
PRINTF("please input enter start send data1:\r\n");
ch = GETCHAR();
cnt = 0;
}
}
}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include<sys/time.h>
#define DEV_SPI "/dev/spidev0.0"
#define LEN 8
int main(int argc,char* argv[])
{
int fd;
int ret;
unsigned char * cmp="genv";
unsigned char mode = 0;
unsigned char bits_per_word = 8;
unsigned int speed = 1000000;
unsigned int cnt = 0;
unsigned int cnt_lost = 0;
unsigned int cnt_error = 0;
unsigned int cntrecv = 0;
unsigned int cntrecvtotal = 200000;
unsigned char *cntr= (unsigned char *)&cntrecv;
unsigned int previous_index=0;
int i = 0;
unsigned char rx_buf[8]={0};
struct spi_ioc_transfer msg = {
.rx_buf = (unsigned long)rx_buf,
.len = 8,
};
struct timeval tv;
unsigned long long millisecondsSinceEpoch_s = 0;
unsigned long long millisecondsSinceEpoch_e = 0;
fd = open(DEV_SPI, O_RDONLY);
if(fd < 0)
{
perror("Open:");
return -1;
}
#if 1
ret = ioctl(fd, SPI_IOC_RD_MODE,&mode);
if(ret < 0)
{
perror("ioctl SPI_IOC_RD_MODE");
return -1;
}
#endif
#if 1
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits_per_word);
if (ret < 0) {
perror("ioctl SPI_IOC_RD_BITS_PER_WORD");
return -1;
}
#endif
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
perror("can't get max speed hz");
for(;;)
{
memset(rx_buf, 0, 8);
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &msg);
if(ret < 1 ){
continue;
}else{
ret=memcmp(rx_buf,cmp,4);
if(ret){
cnt_error++;
continue;
}else{
cntr[0]=rx_buf[4];
cntr[1]=rx_buf[5];
cntr[2]=rx_buf[6];
cntr[3]=rx_buf[7];
if(cntrecv < previous_index || cntrecv > cntrecvtotal ){
cnt_lost++;
continue;
}else{
previous_index = cntrecv;
//printf("cntrecv=%d\n ",cntrecv);
cnt++;
}
//printf("cntrecv=%d\n",cntrecv);
if(cntrecv == 1){
gettimeofday(&tv, NULL);
millisecondsSinceEpoch_s =(unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000;
//printf("SPI TEST START:%llu\n", millisecondsSinceEpoch_s);
}
if(cntrecv >= cntrecvtotal){
gettimeofday(&tv, NULL);
millisecondsSinceEpoch_e =(unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000;
printf("SPI TEST OVER total:%u, lost:%d, error:%d, time:%llu ms\n", cnt,cnt_lost,cnt_error,millisecondsSinceEpoch_e - millisecondsSinceEpoch_s);
break;
}
}
}
}
close(fd);
}