OpenEdv-开源电子网

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

用GPIO模拟SPI,但不知道哪里出了问题- -

[复制链接]

1

主题

2

帖子

0

精华

新手上路

积分
24
金钱
24
注册时间
2018-8-2
在线时间
10 小时
发表于 2018-11-11 17:43:39 | 显示全部楼层 |阅读模式
9金钱



主机程序spi.h

#ifndef _SPI_H
#define _SPI_H
#include "sys.h"


#define MOSI PHout(3)   //主机输出,从机输入
#define SCLK PHout(4)   //时钟引脚
#define MISO PBin(2)        //主机输入,从机输出
#define CS1  PAout(4)          //片选信号1
#define CS2  PAout(5)        //片选信号2

void SPI_Init(void);
u8 SPI_ReadWrite(u8 dat, u8 choice);

#endif


spi.c

#include "spi.h"
#include "delay.h"

////////////////////////////////////////////////////////////////////////////////
//我们标定CPOL和CPHA都为0
//CPOL=0:时钟空闲时电平为低;
//CPHA=0;时钟周期的上升沿发送数据,时钟周期的下降沿接收数据;

////////////////////////////////////////////////////////////////////////////////


//SPI IO初始化
void SPI_Init(void)
{
    GPIO_InitTypeDef GPIO_Initure;
    __HAL_RCC_GPIOB_CLK_ENABLE();           //开启GPIOB时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();           //开启GPIOA时钟
        __HAL_RCC_GPIOH_CLK_ENABLE();                                                        //开启GPIOH时钟
    GPIO_Initure.Pin=GPIO_PIN_3|GPIO_PIN_4; //PH3,4
    GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
    GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
    HAL_GPIO_Init(GPIOH,&GPIO_Initure);
       
        GPIO_Initure.Pin=GPIO_PIN_4|GPIO_PIN_5; //PA4,5
    GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
    GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
    HAL_GPIO_Init(GPIOA,&GPIO_Initure);

    GPIO_Initure.Pin=GPIO_PIN_2;            //PB2
    GPIO_Initure.Mode=GPIO_MODE_INPUT;      //输入
    GPIO_Initure.Pull=GPIO_PULLUP;        //下拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
    HAL_GPIO_Init(GPIOB,&GPIO_Initure);
       
       
       
    HAL_GPIO_WritePin(GPIOH,GPIO_PIN_3,GPIO_PIN_SET);        //PH3置1
    HAL_GPIO_WritePin(GPIOH,GPIO_PIN_4,GPIO_PIN_SET);        //PH4置1  
               
        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);        //PA4置1
    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);        //PA5置1  
}

//延时函数封装:可以进行改变延时的大小
void Delay()
{
        delay_us(10);                //延时1ms
}

//spi实现读写函数
//dat:要发送的数据
//choice: 选择的spi器件
u8 SPI_ReadWrite(u8 dat, u8 choice)
{
        u8 temp;
        SCLK = 0;        //空闲时为低电平
        Delay();
       
        if (choice == 1)
        {
                CS1 = 0;        //置低
        }
        else
        {
                CS2 = 0;   //置低
        }
        Delay();

       
        for (int i = 0; i < 8; i++)
        {
                MOSI = dat & 0x01;        //低位在前
                dat >>= 1;
                SCLK = 1;        //数据在上升沿时,主机发送数据,从机读取数据
                Delay();
                temp >>= 1;                //下降沿时开始读取数据
                if (MISO)temp++;
                SCLK = 0;
                Delay();       
        }
       
        //片选关闭,不再选择
        CS1 = 1;
        CS2 = 1;
        Delay();
       
        return temp;
}



从机程序
spi.h
#ifndef _SPI_H
#define _SPI_H
#include "sys.h"


#define MOSI PHin(3)   //主机输出,从机输入
#define SCLK PHin(4)   //时钟引脚
#define MISO PBout(2)        //主机输入,从机输出
#define CS1  PAin(4)          //片选信号1

void SPI_Init(void);
u8 SPI_ReadWrite(u8 dat);

#endif


spi.c

#include "spi.h"
#include "delay.h"

////////////////////////////////////////////////////////////////////////////////
//我们标定CPOL和CPHA都为0
//CPOL=0:时钟空闲时电平为低;
//CPHA=0;时钟周期的上升沿采集数据,时钟周期的下降沿接收数据;

////////////////////////////////////////////////////////////////////////////////
//从机PB3输入,PB2,PH4输出

//SPI IO初始化
void SPI_Init(void)
{
       GPIO_InitTypeDef GPIO_Initure;
    __HAL_RCC_GPIOB_CLK_ENABLE();           //开启GPIOB时钟
                __HAL_RCC_GPIOA_CLK_ENABLE();           //开启GPIOA时钟
                __HAL_RCC_GPIOH_CLK_ENABLE();                                                        //开启GPIOH时钟
    GPIO_Initure.Pin=GPIO_PIN_3|GPIO_PIN_4; //PH3
    GPIO_Initure.Mode=GPIO_MODE_INPUT;  //推挽输入
    GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
    HAL_GPIO_Init(GPIOH,&GPIO_Initure);
       
                GPIO_Initure.Pin=GPIO_PIN_4; //PA4
    GPIO_Initure.Mode=GPIO_MODE_INPUT;  //推挽输入
    GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
    HAL_GPIO_Init(GPIOA,&GPIO_Initure);

    GPIO_Initure.Pin=GPIO_PIN_2;            //PB2
    GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;      //输出
    GPIO_Initure.Pull=GPIO_PULLUP;        //上拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;     //高速
    HAL_GPIO_Init(GPIOB,&GPIO_Initure);
       

    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_SET);        //PB3   HIGH  

                HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);//PA4   LOW
}

//延时函数封装:可以进行改变延时的大小
void Delay()
{
        delay_us(10);                //延时1ms
}

//spi实现读写函数
//dat:要发送的数据

u8 SPI_ReadWrite(u8 dat)
{
        u8 temp=0;

        Delay();
       
               
        for (int i = 0; i < 8; i++)
        {
               
                //数据在上升沿读取,在下降沿发送
                if(SCLK==1)
                {
                       
                        Delay();
       
                        if (MOSI)temp++;       
                       
                        temp <<= 1;       
               
               
        }
                else if(SCLK==0)
                {
                        Delay();
                        if(dat&&0x80)MISO=1;
                        else MISO=0;
                        dat<<=1;
                       
                }
       
        }
               
        return temp;
}


最佳答案

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

细看你程序好像有很大问题啊,时钟同主机发送没错,从机要判断时钟上升沿和下降沿,而不是高电平和低电平,而上长沿和下降沿要用外部中断来获取的,从机不能用查询的方式判断上升沿和下降沿。 还有你的主机发数是低位先发,从机却把先收到的放在了高位,真是看不明白了
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

3

主题

29

帖子

0

精华

初级会员

Rank: 2

积分
116
金钱
116
注册时间
2015-1-15
在线时间
18 小时
发表于 2018-11-11 17:43:40 | 显示全部楼层
chaojunchi 发表于 2018-11-11 20:12
for (int i = 0; i < 8; i++)
        {
                MOSI = dat & 0x01;        //低位在前 ...

细看你程序好像有很大问题啊,时钟同主机发送没错,从机要判断时钟上升沿和下降沿,而不是高电平和低电平,而上长沿和下降沿要用外部中断来获取的,从机不能用查询的方式判断上升沿和下降沿。
还有你的主机发数是低位先发,从机却把先收到的放在了高位,真是看不明白了
回复

使用道具 举报

33

主题

1628

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
6679
金钱
6679
注册时间
2015-8-25
在线时间
1036 小时
发表于 2018-11-11 20:00:09 | 显示全部楼层
SPI没模拟过从机,帮顶
回复

使用道具 举报

3

主题

29

帖子

0

精华

初级会员

Rank: 2

积分
116
金钱
116
注册时间
2015-1-15
在线时间
18 小时
发表于 2018-11-11 20:12:45 | 显示全部楼层
本帖最后由 chaojunchi 于 2018-11-11 20:22 编辑

        for (int i = 0; i < 8; i++)
        {
                MOSI = dat & 0x01;        //低位在前
                dat >>= 1;
                SCLK = 1;        //数据在上升沿时,主机发送数据,从机读取数据
                Delay();
                temp >>= 1;                //下降沿时开始读取数据
                if (MISO)temp++;
                SCLK = 0;
                Delay();        
        }
你这个temp应该是temp<<=1;否则只能返回0和1两个值,不会有其它值。这样先进的是高位。如果你想先读进的是低位,那程序得改一下了。
       temp = 0;
       for(int i = 0;i < 8;i++)
       {
              MOSI = dat & 0x01;
              dat >> 1;
              SCLK = 1;
              Delay();
              if(MISO)temp1 = 1 << i;else temp1 = 0;
              temp |= temp1;
              SCLK = 0;
              Delay();
        }
回复

使用道具 举报

3

主题

1907

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
4106
金钱
4106
注册时间
2018-8-14
在线时间
696 小时
发表于 2018-11-11 20:48:27 | 显示全部楼层
一) 主机控制CLK, 从机检测CLK, 你刚好反了
二)  if (MISO)temp++;  应该是  if (MISO)temp|=0x80;
回复

使用道具 举报

11

主题

1044

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3722
金钱
3722
注册时间
2011-5-23
在线时间
2013 小时
发表于 2018-11-11 22:52:01 | 显示全部楼层
果断接上逻辑分析仪啊
RT-Thread RTOS 音频,WIFI,蓝牙
回复

使用道具 举报

1

主题

2

帖子

0

精华

新手上路

积分
24
金钱
24
注册时间
2018-8-2
在线时间
10 小时
 楼主| 发表于 2018-11-13 15:42:28 | 显示全部楼层
chaojunchi 发表于 2018-11-11 20:33
细看你程序好像有很大问题啊,时钟同主机发送没错,从机要判断时钟上升沿和下降沿,而不是高电平和低电平 ...

多谢老哥指点,初学stm32,我自己再改改
回复

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2025-6-8 17:57

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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