OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 7284|回复: 10

STM32作为主机进行SPI通信,取回数据出错,有代码,特求助。

[复制链接]

9

主题

86

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
211
金钱
211
注册时间
2017-6-16
在线时间
46 小时
发表于 2017-12-26 09:37:31 | 显示全部楼层 |阅读模式
5金钱
SPI通信,STM32作为主机,GP21作为从机;主机取出的数据总是为65536.000000.先把GP21相关SPI贴出再贴代码。求助坛友们。
1.png 2.png

以下为相关代码
配置SPI:(STM32为主机,GP21为从机,CPOL=0,CPHA=1.NSS由C7管脚控制)
void SPI2_Configuration(void)           //SPI2  与TDC-GP21通信
  {
        SPI_InitTypeDef        SPI_InitStructure;//声明一个结构体变量       

        SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//双线双向全双工
        SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//主spi
        SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//数据大小:8位帧结构
        SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;//CP0L=0          CLK的初始状态          时钟不是片选
        SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;  //采样延迟 第2个时钟采样         CPHA=1
        SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//软件控制 C7控制
        SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;  //波特率预分频值为36MHZ/256
        SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                  //数据传送从最高位开始
        SPI_InitStructure.SPI_CRCPolynomial = 7;
       
        SPI_Init(SPI2,&SPI_InitStructure);
        SPI_Cmd(SPI2,ENABLE);//使能SPI2       
  }


SPI读写函数:
u8 SPI2_WriteReadData(u8 dat)                  //SPI读写函数         片选放在里面一次性操作 别的函数里不用单独操控了
{
        u8 readdata;
        u16 i = 0;
        /* 当发送缓冲器空 */
        GPIO_ResetBits(GPIOC, GPIO_Pin_7); delay(5);//打开片选
        while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)  //如果得到的发送缓冲器为0 表示不空 则不发送数据只是进行i++等待发送缓冲器为1 为1 while不成立跳出循环执行发送语句
                {
                        i++;
                        if(i > 10000)
                                {printf("发送失败");return 0x01;}
                }
        /* 发送数据*/
        SPI_I2S_SendData(SPI2, dat);
        /* 等待接收缓冲器为非空 */
        i=0;
        while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)
                {
                        i++;
                        if(i > 10000)
                                {printf("接收失败");return 0x01;}
                }
        //返回1则为非空while不成立跳出while将非空的数据读出来 如果空了则返回0 while成立进行循环不读数
        /* 将读取到的数值返回 */
        readdata=SPI_I2S_ReceiveData(SPI2);  
        GPIO_SetBits(GPIOC, GPIO_Pin_7);delay(10); //关闭片选
        return readdata;        
}


SPI写32位数和读32位数:(写是配置GP21相关功能,读是读取GP21测量数据)
void SPI_WRITE_DATA32(u32 data)  //写32位数对GP21进行配置 前8位地址后24位配置
{
        SPI2_WriteReadData((u8)((data) >> 24)); //32位右移24位是将高8位移到低8位上并截断 然后按照从高位到低位发送
        SPI2_WriteReadData((u8)((data) >> 16));//连续发送32位数据对GP21进行配置
        SPI2_WriteReadData((u8)((data) >> 8));
        SPI2_WriteReadData((u8)data);
}

float SPI_READ32_DATA(u8 ml)           //读32位数据  GP21测量数据格式为32位,其中前16位为整数后16位为小数
{
        u16 data1=0x0000,data2=0x0000;
        float data3=0;
        SPI2_WriteReadData(ml);
        data1|= SPI2_WriteReadData(0xFF)<<8;        //先移位再位或
        data1|= SPI2_WriteReadData(0xFF);
        data2|= SPI2_WriteReadData(0xFF)<<8;
        data2|= SPI2_WriteReadData(0xFF);

        data3=(float)data1+(float)data2/65536;       
        return data3;
}


GPIO配置:(B13 14 15为SPI2管脚。C7控制片选SSN)
GPIO_InitTypeDef    GPIO_InitStructure;       

                        GPIO_InitStructure.GPIO_Pin =GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;   //SPI2管脚
                        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          //复用功能输出 B14与外设连接 由外设决定
            GPIO_Init(GPIOB, &GPIO_InitStructure);         

                        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_6;         //C7控制TDC-GP21的SPI片选CS   C6控制led点灯
                        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;         
            GPIO_Init(GPIOC, &GPIO_InitStructure);


串口中断函数:(当作一个开关触发测量。shice初始为0主函数中if不成立,串口改变shice为1使if成立)
void USART1_IRQHandler(void)
{
        int ReceiveDate=1;                        //定义一个变量存放数据
        GPIO_ResetBits(GPIOC, GPIO_Pin_6);
        delay(10000);
        if(!(USART_GetITStatus(USART1,USART_IT_RXNE))); //读取接收中断标志位USART_IT_RXNE
                                                        //USART_FLAG_RXNE:接收数据寄存器非空标志位
                                                        //1:忙状态  0:空闲(没收到数据,等待。。。)
        {
                  USART_ClearITPendingBit(USART1, USART_IT_RXNE);        //清除中断标志
                  SPI2_WriteReadData(0xB5);  //0xB5为读GP21寄存器REG_1高8位,用来测试SPI通信
                  ReceiveDate=SPI2_WriteReadData(0xFF);
                 
                  shice=0;GPIO_SetBits(GPIOC, GPIO_Pin_7);//  这时shice还不能为别的数 因为还没有配置完成
                  GPIO_init();GP2_init();delay(10);        //GPIO中执行电平复位并拉高SPI片选CS端          GP2中执行相关寄存器配置          
                  kflag=0;cs=10;delay(5);         //设置一次测量多少数据
                   pj=0;memset(c1,0,sizeof(c1));c2=0;cnt1=0;cnt2=0; delay(1);        //对相关统计数进行清0
              shice=1;t0=1;                                        //打开shice使主函数中的if成立
          
              printf("\nREG_1-8=%d\n",ReceiveDate); printf("cnt=%d\n",cs);
                  GPIO_SetBits(GPIOC, GPIO_Pin_6);
        }  
}


主函数:
int main(void)
{
   RCC_Configuration();
   GPIO_Configuration();
   TIM2_Configuration();
   USART_Configuration();
   SPI2_Configuration();
   NVIC_Configuration();

   GPIO_SetBits(GPIOC, GPIO_Pin_7);      //SPI片选管脚先为1 不打开CS
   GPIO_init();          //电源reset  
   GP2_init();         //配置GP21相关         设置测量范围 通道 晶振 校准等
   SPI2_WriteReadData(0x70);delay(1);    //0x70给GP21初始化
   GPIO_SetBits(GPIOC, GPIO_Pin_0|GPIO_Pin_1);        //使能start和stop1管脚
   GPIO_SetBits(GPIOC, GPIO_Pin_7);      //SPI片选管脚先为1 不打开CS

   while(1)                 //  串口执行完后kflag=0,cs=10 ;shice=1;t0=1; if成立开始打印测量数据
           {       
                if((shice!=0)&&(t0==1 ))        //t0初始为1 shice初始为0 不成立 在串口里修改为1后才成立
             {       
                         SPI2_WriteReadData(0x70);delay(1);    // 初始化
                ret=shishicl();//时间测量        float型浮点数         调用之后SSN=1          kflag在累加   每用完一次后t0都被置1使if一直成立                           
                        if(kflag==cs)                         //cs是设置的测量次数         kflag在shishicl函数里累加 一直加到cs停止
                    { t0=0;kflag=0;
                                  pj=pj/cnt1;p=cnt1;printf("pj=%f\n cnt1=%d\n",pj,cnt1);  //数据处理
                                  while(p--)
                                           { if((c1[p]>(pj-0.0035))&&(c1[p]<(pj+0.0035)))
                                                        {c2+=c1[p];cnt2++;}                                                 
                                     }
                              printf("pj=%f\n cnt2=%d\n",c2/cnt2,cnt2);
                         }                       
                    pj+=ret;c1[cnt1]=ret;cnt1+=1;   
                    printf("%f\n",ret);
             }               
        }
}

测试结果:(串口打印)
REG_1-8=255
cnt=10
65536.000000
65536.000000
65536.000000
65536.000000
65536.000000
65536.000000
65536.000000
65536.000000
65536.000000
pj=65536.000000
cnt1=9
pj=65536.000000
cnt2=9
65536.000000
修改了好几次都是这个数,尝试把MOSI和MISO对调结果变为全0.
我自己感觉问题可能是出在写和读32位数函数那里有问题;GP21大多要读写32位,而STM32为8位,读写32位是参考了开发板历程的程序。

我之前用的51单片机进行SPI通信完全没问题,测试结果良好;感觉STM32的SPI有点复杂,而且帧格式只支持8或16位。
希望路过的朋友指点迷津。


最佳答案

查看完整内容[请看2#楼]

反复调试,问题已解决。 发现读32位数据和写32位数据,不能向上面函数这样写。 发送时需要把32位截断成4个8位分开4次发送 接收的时候也是分4次接收各8位之后在拼接
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

9

主题

86

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
211
金钱
211
注册时间
2017-6-16
在线时间
46 小时
 楼主| 发表于 2017-12-26 09:37:32 | 显示全部楼层
反复调试,问题已解决。

发现读32位数据和写32位数据,不能向上面函数这样写。
发送时需要把32位截断成4个8位分开4次发送
接收的时候也是分4次接收各8位之后在拼接

回复

使用道具 举报

9

主题

86

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
211
金钱
211
注册时间
2017-6-16
在线时间
46 小时
 楼主| 发表于 2017-12-26 09:52:53 | 显示全部楼层
我把B13 14 15这3根线全拔掉了  结果还是65536.真是醉了,代码打包传上来了,希望大家帮帮忙看下

SPI通信.zip

1.07 MB, 下载次数: 138

回复

使用道具 举报

0

主题

113

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3321
金钱
3321
注册时间
2017-12-14
在线时间
406 小时
发表于 2017-12-26 10:09:04 | 显示全部楼层
建议你接上逻辑分析仪看看具体波形,因为有可能配置不符
回复

使用道具 举报

9

主题

86

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
211
金钱
211
注册时间
2017-6-16
在线时间
46 小时
 楼主| 发表于 2017-12-26 10:31:22 | 显示全部楼层
feichangkunge 发表于 2017-12-26 10:09
建议你接上逻辑分析仪看看具体波形,因为有可能配置不符

逻辑分析仪听说过,和示波器区别大不,目前没有这种设备,只有示波器,还是单通道的
回复

使用道具 举报

50

主题

1805

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
6662
金钱
6662
注册时间
2016-5-29
在线时间
910 小时
发表于 2017-12-26 10:43:43 | 显示全部楼层
魔双月壁 发表于 2017-12-26 10:31
逻辑分析仪听说过,和示波器区别大不,目前没有这种设备,只有示波器,还是单通道的

区别可大了.
用了逻辑分析仪,基本上不想使用示波器了.硬件工程师要看模拟信号才用示波器.
回复

使用道具 举报

0

主题

113

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3321
金钱
3321
注册时间
2017-12-14
在线时间
406 小时
发表于 2017-12-26 10:49:17 | 显示全部楼层
魔双月壁 发表于 2017-12-26 10:31
逻辑分析仪听说过,和示波器区别大不,目前没有这种设备,只有示波器,还是单通道的

你连接读写32位数据时,把选片放外面,不要写在u8 SPI2_WriteReadData(u8 dat) 这个函数里,不然数据会不正常的
回复

使用道具 举报

9

主题

86

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
211
金钱
211
注册时间
2017-6-16
在线时间
46 小时
 楼主| 发表于 2017-12-26 14:15:26 | 显示全部楼层
feichangkunge 发表于 2017-12-26 10:49
你连接读写32位数据时,把选片放外面,不要写在u8 SPI2_WriteReadData(u8 dat) 这个函数里,不然数据会不 ...

片选拿出来之后,有一点正常反应了。串口里发送0xB5读数据有返回值85.主函数里的读数还是65536.000000.
                  SPI2_WriteReadData(0xB5);
                  ReceiveDate=SPI2_WriteReadData(0xFF);
       
回复

使用道具 举报

9

主题

86

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
211
金钱
211
注册时间
2017-6-16
在线时间
46 小时
 楼主| 发表于 2017-12-26 14:27:34 | 显示全部楼层
USART_ClearITPendingBit(USART1, USART_IT_RXNE);        //清除中断标志
                  GPIO_ResetBits(GPIOC, GPIO_Pin_7);delay(1);
                  SPI2_WriteReadData(0xB5);
                  ReceiveDate=SPI2_WriteReadData(0xFF);
                  GPIO_SetBits(GPIOC, GPIO_Pin_7);delay(1);

                  GPIO_ResetBits(GPIOC, GPIO_Pin_7);delay(1);
                  SPI2_WriteReadData(0xB7);
                  id1|= SPI2_WriteReadData(0xFF);
                  id2|= SPI2_WriteReadData(0xFF);
                  GPIO_SetBits(GPIOC, GPIO_Pin_7);delay(1);

现在调试程序发现串口里发送的0xB5和0xB7均能得到正确的返回值:
REG_1-8=85
id1=129
id2=0
cnt=10
但是读32位的数就有问题,还是返回65536.00000错误值;怀疑是读32位的那个函数有问题,继续调试。
1.png 2.png
1.png
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2017-12-27 01:18:19 | 显示全部楼层
魔双月壁 发表于 2017-12-26 17:00
反复调试,问题已解决。

发现读32位数据和写32位数据,不能向上面函数这样写。

谢谢分享
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
2
金钱
2
注册时间
2019-7-27
在线时间
0 小时
发表于 2019-7-27 10:33:44 | 显示全部楼层
想问一下,主函数里c1[p]是什么
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-26 14:29

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表