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 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);
}

猜你喜欢

转载自blog.csdn.net/TSZ0000/article/details/123848194
SPI