参考文章《SPI》
原理图
GPIO 可以通过 IOF0 和 IOF1 功能,使得 SOC 中的外设能够复用 GPIO 的 32 根引脚与芯片外设进行通信,其接口分配表如下所示:
GPIO编号 | IOF0 | IOF1 |
---|---|---|
GPIO0 | - | PWM0_0 |
GPIO1 | - | PWM0_1 |
GPIO2 | QSPI1:SS0 | PWM0_2 |
GPIO3 | QSPI1:SD0/MOSI | PWM0_3 |
GPIO4 | QSPI1:SD1/MISO | - |
GPIO5 | QSPI1:SCK | - |
GPIO6 | QSPI1:SD2 | - |
GPIO7 | QSPI1:SD3 | - |
GPIO8 | QSPI1:SS1 | - |
GPIO9 | QSPI1:SS2 | - |
GPIO10 | QSPI1:SS3 | PWM2_0 |
GPIO11 | - | PWM2_1 |
GPIO12 | IIC:SDA | PWM2_2 |
GPIO13 | IIC:SCL | PWM2_3 |
GPIO14 | - | - |
GPIO15 | - | - |
GPIO16 | UART0:RX | - |
GPIO17 | UART0:TX | - |
GPIO18 | - | - |
GPIO19 | - | PWM1_1 |
GPIO20 | - | PWM1_0 |
GPIO21 | - | PWM1_2 |
GPIO22 | - | PWM1_3 |
GPIO23 | - | - |
GPIO24 | UART1:RX | - |
GPIO25 | UART1:TX | - |
GPIO26 | QSPI2:SS | - |
GPIO27 | QSPI2:SD0/MOSI | - |
GPIO28 | QSPI2:SD1/MISO | - |
GPIO29 | QSPI2:SCK | - |
GPIO30 | QSPI2:SD2 | - |
GPIO31 | QSPI2:SD3 | - |
源码
嵌入式软件通过SPI发送十进制数据 “33” 给底层硬件
/*
* spi.c
*
* Created on: 2020年7月14日
* Author: anytao
*/
#include <stdio.h>
#include <stdlib.h>
#include "platform.h"
#include <string.h>
#include "plic/plic_driver.h"
#include "encoding.h"
#include <unistd.h>
#include "stdatomic.h"
#define SPI2_MOSI_GPIO_OFFSET 27
#define SPI2_MISO_GPIO_OFFSET 28
#define SPI2_SCK_GPIO_OFFSET 29
#define SPI2_SCKMODE_CPOL 1
#define SPI2_SCKMODE_CPHA 0
#define SPI2_CS_GPIO_OFFSET 26 //
#define DATA_SIZE 20
int8_t spi_tx_data[DATA_SIZE];
void spi_init2()
{
//GPIO IOF0 //SPI2 IOF0_SPI2_MASK
GPIO_REG(GPIO_IOF_SEL)&= ~ ((0x1 << SPI2_MOSI_GPIO_OFFSET) |(0x1 << SPI2_SCK_GPIO_OFFSET)|(0x1 << SPI2_MISO_GPIO_OFFSET));
//GPIO IOF0 Enable //SPI2 IOF (0x1 << SPI2_CS_GPIO_OFFSET)
GPIO_REG(GPIO_IOF_EN)|= ((0x1 << SPI2_MOSI_GPIO_OFFSET)|(0x1 << SPI2_SCK_GPIO_OFFSET) |(0x1 << SPI2_MISO_GPIO_OFFSET));
//禁止MOSI,SCK,CS的输入
GPIO_REG(GPIO_INPUT_EN) &= ~((0x1 << SPI2_MOSI_GPIO_OFFSET) | (0x1 << SPI2_SCK_GPIO_OFFSET)|(0x1 << SPI2_CS_GPIO_OFFSET));
//使能MOSI,SCK,CS的输出
GPIO_REG(GPIO_OUTPUT_EN) |= ((0x1 << SPI2_MOSI_GPIO_OFFSET) | (0x1 << SPI2_SCK_GPIO_OFFSET)|(0x1 << SPI2_CS_GPIO_OFFSET));
//禁止MMISO的输出
GPIO_REG(GPIO_OUTPUT_EN) &= ~(0x1 << SPI2_MISO_GPIO_OFFSET);
//使能MMISO的输入
GPIO_REG(GPIO_INPUT_EN) |= (0x1 << SPI2_MISO_GPIO_OFFSET);
//配置时钟寄存器:SCK的极性和相位,极性为0表示空闲状态为0,时钟的“前沿”为上升沿
//相位为0,则数据在发送端的时钟后沿改变,在接收端的下一个时钟前沿被采样
SPI2_REG(SPI_REG_SCKMODE) &= ~((0x1 << SPI2_SCKMODE_CPOL)|(0x1 << SPI2_SCKMODE_CPHA));
//配置时钟频率分频系数寄存器
//若SPI所处时钟域Freq_SPI
//则分频系数=Freq_SPI/(2*(div+1))
SPI2_REG(SPI_REG_SCKDIV)=0x08;
}
void Delay(unsigned int time){
volatile unsigned int repeatcnt=0;
repeatcnt=time;
while(repeatcnt--);
}
uint8_t SPI_Send_ReadByte(uint8_t dat) //SPI写数据,返回SPI的读数据
{
SPI2_REG(SPI_REG_TXFIFO) = dat;
Delay(50);
return SPI2_REG(SPI_REG_RXFIFO)&0xff;
}
void main(){
int i=0;
//初始化SPI发送的数组
for(i=0;i<DATA_SIZE;i=i+1){
spi_tx_data[i]=i;
}
printf("spi is start");
//SPi初始化
spi_init2();
//CS片选拉低
GPIO_REG(GPIO_OUTPUT_VAL)&=~(0x1<<SPI2_CS_GPIO_OFFSET);
Delay(50);
//SPI发送数据
SPI_Send_ReadByte(33);
//CS片选拉高
Delay(50);
GPIO_REG(GPIO_OUTPUT_VAL)|=(0x1<<SPI2_CS_GPIO_OFFSET);
}
实验结果
在 verilog 中添加 SPI 解串程序,也就是将 MOSI 发出来的串行信号进行解串。
通过加入ILA可以看到发送的数据如下图所示。
可以看到数据从 MSB 到 LSB 被解串出来,分别为0-0-1-0-0-0-0-1,对应十进制数 “33”
工程获取
嵌入式spi.rar:嵌入式软件的工程(window环境:Eclipse+OpenOCD)
FPGA_nucleikit_spi.rar:FPGA程序
获取资料方法一:集赞
关注小编公众号后,将本文转发至朋友圈,集齐6个赞,截图发送到后台,小编会在24小时之内回复。备注:【领取RISCV SPI工程】即可领取资料
获取资料方法二:转发群
关注小编公众号后,将本文转发至不低于100人的群(RISCV相关行业),截图发送到后台,小编会在24小时之内回复。备注:【领取RISCV SPI工程】即可领取资料