本文主要是介绍spi cpol极性和cpha相位,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
在spi通信中,需要设置cpol极性和cpha相位,保持master和slave两端时钟一致,相互匹配才能正常通信。主要原因是spi没有握手信号不可靠传输。
参考文章:高手带你理解SPI中的极性CPOL和相位CPHA
名词解释
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 thefollowing edge.*/kLPSPI_ClockPhaseSecondEdge = 1U /*!< CPHA=1. Data is changed on the leading edge of the SCK and captured on thefollowing 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;elsetemp |= 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:26063671SPI TEST OVER:total 199974, 0, 15, packages,time:25902166SPI TEST OVER:total 199979, 0, 11, packages,time:25812487SPI TEST OVER:total 199959, 0, 22, packages,time:25739095【T=100us】【cpha=1】SPI TEST OVER total:199994, lost:0, error:3, time:40820 msSPI TEST OVER total:199993, lost:0, error:5, time:40819 msSPI 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 8int 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 1ret = ioctl(fd, SPI_IOC_RD_MODE,&mode);if(ret < 0){perror("ioctl SPI_IOC_RD_MODE");return -1;}
#endif
#if 1ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits_per_word);if (ret < 0) {perror("ioctl SPI_IOC_RD_BITS_PER_WORD");return -1;}
#endifret = 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);
}
这篇关于spi cpol极性和cpha相位的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!