图5.SCK高电平空闲且第二个沿采样
SPI配置
主模式
通过配置波特率发生器(SPI_I2S_SPBREG)设定串行时钟波特率,公式为:波特率=
FPClk/SPBRG (fpclk是APB时钟
频率)。配置通用控制寄存器(SPI_I2S_CCTL)的SPI数据宽度位(SPILEN),决定数据帧的长度是7位还是8位;配置时钟相位选择位(CPHA)与时钟极性标志位(CPOL),确定时序模式,为保证数据正常传输,主从设备的时序模式应保持配置一致;配置LSB在前使能位(LSBFE),决定数据位的输出顺序。操作全局控制寄存器(SPI_I2S_GCTL)的DW8_32位进行发送和接收数据寄存器有效数据选择,可配置为只有低8位有效或32位数据都有效;操作主机模式位(MODE)为1,选择主机模式;配置SPI/I2S选择位(SPIEN)为1,使能SPI。
若只接收而不发送数据,则配置接收数据个数寄存器(SPI_I2S_RXDNR),定义下次接收过程中需要接收字节的个数。
从模式
配置通用控制寄存器(SPI_I2S_CCTL)的LSB在前使能位(LSBFE),决定数据位的输出顺序是从最低有效位到最高有效位或从最高有效位到最低有效位;配置SPI数据宽度位(SPILEN),决定数据帧的长度是7位还是8位;配置时钟相位选择位(CPHA)与时钟极性标志位(CPOL),决定时序模式。配置全局控制寄存器(SPI_I2S_GCTL)的主机模式位(MODE)为0,选择从机模式;配置SPI/I2S选择位(SPIEN)为1,使能SPI。
数据发送
主模式
将需要发送的数据写入发送数据寄存器(SPI_I2S_TXREG),该寄存器的有效位由全局控制器(SPI_I2S_GCTL)的DW8_32位控制。在发送第一个数据位时,整个数据被传输到移位寄存器,后续数据通过移位寄存器串行输出到MOSI引脚。当中断状态寄存器(SPI_I2S_INTSTAT)的发送缓冲器有效中断标志位(TX_INTF)被置1,数据已从发送缓冲器被传输到移位寄存器。
从模式
当从设备收到SCK传来的时钟信号,同时接收到MOSI引脚传输的第一个数据位,从设备开始发送,第一个位被发送到MISO引脚,其余bit位被传输到移位寄存器,通过移位寄存器将数据串行发送。当中断状态寄存器(SPI_I2S_INTSTAT)的发送缓冲器有效中断标志位(TX_INTF)被置1,表示第一位已发送,其余位被传输到移位寄存器。
数据接收
主模式
从MISO引脚接收数据,数据通过移位寄存器,在最后一个采样时钟边沿后,数据字节被传输到接收缓冲器中。当中断状态寄存器(SPI_I2S_INTSTAT)的接收端数据有效中断标志位(RX_INTF)置1,数据接收完成,主模式下不再发送时钟信号。
从模式
从MOSI引脚接收数据,数据通过移位寄存器,在最后一个采样时钟边沿后,数据字节被传输到接收缓冲器中。当中断状态寄存器(SPI_I2S_INTSTAT)的接收端数据有效中断标志位(RX_INTF)置1,数据接收完成。
实验
本实验为回环
测试,通过使用杜邦线连接SPI的MISO与MOSI引脚,实现数据的发送与接收。配置SPI主机,SPI进行一次数据发送与接收并对发送与接收信息进行验证,并通过串口打印传输情况,若有发送与接收数据不同的情况,串口打印出错信息与出错个数,若验证成功则打印"spi loopback xfer done."。
启用外设时钟 enable_
clock()
实验使用SPI1,且需要通过串口打印实验现象,因此需启用SPI1与UART的外设时钟。
{
/* Enable UART1 clock. */
RCC->APB2ENR |= RCC_APB2_PERIPH_UART1;
/* Enable GPIOA clock. */
RCC->AHB1ENR |= RCC_AHB1_PERIPH_GPIOA;
/* Enable SPI1 clock. */
RCC->APB2ENR |= RCC_APB2_PERIPH_SPI1;
}
配置引脚 pin_init()
配置SPI的NSS(PA4)、MOSI(PA7)、MISO(PA6)、SCK(PA5)引脚,因为实验现象通过串口显示,所以配置UART的TX(PA9)与RX(PA10)引脚。
void pin_init()
{
/* Setup NSS(PA4). */
GPIOA->CHL &= ~GPIO_CRL_MODE4_MASK;
GPIOA->CHL |= (GPIO_PinMode_AF_PushPull << GPIO_CRL_MODE4_SH
IFT); /* PA4 mul
tiplexed push-pull output. */
GPIOA->AFRL &= ~GPIO_AFRL_AFR_MASK;
GPIOA->AFRL |= (GPIO_AF_0 << GPIO_CRL_MODE4_SHIFT); /* Use AF0. */
/* Setup MOSI(PA7). */
GPIOA->CHL &= ~GPIO_CRL_MODE7_MASK;
GPIOA->CHL |= (GPIO_PinMode_AF_PushPull << GPIO_CRL_MODE7_SHIFT); /* PA7 multiplexed push-pull output. */
GPIOA->AFRL |= (GPIO_AF_0 << GPIO_CRL_MODE7_SHIFT); /* Use AF0. */
/* Setup MISO(PA6). */
GPIOA->CHL &= ~GPIO_CRL_MODE6_MASK;
GPIOA->CHL |= (GPIO_PinMode_In_Floating << GPIO_CRL_MODE6_SHIFT); /* PA6 floating input. */
GPIOA->AFRL |= (GPIO_AF_0 << GPIO_CRL_MODE6_SHIFT); /* Use AF0. */
/* Setup SCK(PA5). */
GPIOA->CHL &= ~GPIO_CRL_MODE5_MASK;
GPIOA->CHL |= (GPIO_PinMode_AF_PushPull << GPIO_CRL_MODE5_SHIFT); /* PA5 floating input. */
GPIOA->AFRL |= (GPIO_AF_0 << GPIO_CRL_MODE5_SHIFT); /* Use AF0. */
/* Setup PA9, PA10. */
GPIOA->CRH &= ~GPIO_CRH_MODE9_MASK;
GPIOA->CRH |= (GPIO_PinMode_AF_PushPull << GPIO_CRH_MODE9_SHIFT); /* PA9 multiplexed push-pull output. */
GPIOA->AFRH &= ~GPIO_AFRH_AFR_MASK;
GPIOA->AFRH |= (GPIO_AF_1 << GPIO_CRH_MODE9_SHIFT); /* Use AF1. */
GPIOA->CRH &= ~GPIO_CRH_MODE10_MASK;
GPIOA->CRH |= (GPIO_PinMode_In_Floating << GPIO_CRH_MODE10_SHIFT); /* PA10 floating input. */
GPIOA->AFRH |= (GPIO_AF_1 << GPIO_CRH_MODE10_SHIFT); /* Use AF1. */
}UART初始化 uart_init()初始化UART,配置时钟频率、波特率、数据长度、停止位、传输模式及是否使用校验。void uart_init()
{
/* Clear the corresponding bit to be used. */
UART1->CCR &= ~( UART_CCR_PEN_MASK | UART_CCR_PSEL_MASK | UART_CCR_SPB0_MASK | UART_CCR_SPB1_MASK | UART_CCR_CHAR_MASK );
UART1->GCR &= ~( UART_GCR_AUTOFLOWEN_MASK | UART_GCR_RXEN_MASK | UART_GCR_TXEN_MASK );
/* WordLength. */
UART1->CCR |= UART_CCR_CHAR_MASK;
/* XferMode. */
UART1->GCR |= (UART_XferMode_RxTx << UART_GCR_RXEN_SHIFT);
/* Setup baudrate, BOARD_
debug_UART_FREQ = 48000000u, BOARD_DEBUG_UART_BAUDRATE = 9600u. */
UART1->BRR = (BOARD_DEBUG_UART_FREQ / BOARD_DEBUG_UART_BAUDRATE) / 16u;
UART1->FRA = (BOARD_DEBUG_UART_FREQ / BOARD_DEBUG_UART_BAUDRATE) % 16u;
/* Enable UART1. */
UART1->GCR |= UART_GCR_UARTEN_MASK;
}
SPI初始化 spi_init()
操作全局控制寄存器(SPI_I2S_GCTL)的MODE位,配置SPI为主模式,操作波特率发生器(SPI_I2S_SPBREG)配置波特率为400KHz,总线时钟频率为48MHz,操作通用控制寄存器(SPI_I2S_CCTL)的CPOL位与CPHA位配置通信模式,操作LSB在前使能位(LSBFE)令数据发送或接收最高位在前,操作全局控制寄存器对发送和接收数据寄存器有效数据进行选择(DW8_32),配置为只有低8位有效,设置NSS位置1,使硬件控制主模式下的NSS输出,配TXEN位与RXEN位置1,使能发送与接收;置SPI/I2S选择位(SPIEN)为1,使能SPI。
void spi_init()
{
/* Master. */
SPI1->GCTL = SPI_I2S_GCTL_MODE_MASK; /* Master mode. */
/* XferMode. */
SPI1->GCTL |= (SPI_I2S_GCTL_RXEN_MASK | SPI_I2S_GCTL_TXEN_MASK); /* Enable TX and RX. */
/* AutoCS. */
SPI1->GCTL |= SPI_I2S_GCTL_NSS_MASK; /* NSS select signal that from hardware. */
/* BaudRate. */
SPI1->SPBRG = 120u; /* SPBRG = fpclk / baudrate = 48000000 / 400000 = 120. */
SPI1->CCTL &= ~(SPI_I2S_CCTL_TXEDGE_MASK | SPI_I2S_CCTL_RXEDGE_MASK); /* Sampling data in the middle of transmission data bits. */
/* DataWidth. */
SPI1->GCTL &= ~SPI_I2S_GCTL_DW832_MASK; /* Only the lower 8 bits are valid. */
/* CPOL & CPHA. */
SPI1->CCTL &= ~(SPI_I2S_CCTL_CPHA_MASK | SPI_I2S_CCTL_CPOL_MASK); /* CPOL = 0, CPHA = 0. */
/* LSB first enable bit. */
SPI1->CCTL &= ~SPI_I2S_CCTL_LSBFE_MASK; /* The highest bit of data transmission or reception comes first. */
/* Enbale SPI. */
SPI1->GCTL |= SPI_I2S_GCTL_SPIEN_MASK;
}SPI发送数据 spi_putbyte()当发送缓冲器未满时,将数据传入发送数据寄存器(SPI_I2S_TXRFG),根据初始化配置,数据低8位有效,通过MOSI引脚串行输出。void spi_putbyte(uint8_t c)
{
while (SPI_I2S_CSTAT_TXFULL_MASK & SPI1->CSTAT)
{}
SPI1->TXREG = c;
}
SPI接收数据 spi_getbyte()
当接收端缓冲器接收了一个完整字节时,读接收数据寄存器(SPI_I2S_RXREG),返回接收数据。
uint8_t spi_getbyte()
{
while (0u == (SPI_I2S_CSTAT_RXAVL_MASK & SPI1->CSTAT) )
{}
return SPI1->RXREG;
}
main()函数
main()函数结合上述操作,初始化SPI,定义发送数组spi_tx_buf[16]并赋值,将spi_tx_buf[16]数组中的数值进行发送,定义接收数组spi_rx_buf[16]对数据进行接收。本实验使用杜邦线将MOSI引脚与MISO相连,因此,发送数据与接收数据应相同,将发送数组与接收数组的数值进行比较,定义变量spi_xfer_err_count用于
计数发送与接收数值不同的数据个数。若发送与接收数值相同则串口输出"spi loopback xfer done.",若不同则串口输出传输错误与出错个数。实验现象如图6所示。
int main()
{
enable_clock();
pin_init();
uart_init();
printf("spi_basic example.
");
spi_init();
for (uint32_t i = 0u; i < 16u; i++)
{
spi_tx_buf
= i;
}
/* SPI xfer once. */
for (uint32_t i = 0u; i < 16u; i++)
{
spi_putbyte(spi_tx_buf);
spi_rx_buf = spi_getbyte();
}
/* validation. */
spi_xfer_err_count = 0u;
for (uint32_t i = 0u; i < 16u; i++)
{
if (spi_rx_buf != spi_tx_buf)
{
spi_xfer_err_count++;
}
}
if (spi_xfer_err_count == 0u)
{
printf("spi loopback xfer done.
");
}
else
{
printf("spi loopback xfer error. spi_xfer_err_count = %u
", (unsigned)spi_xfer_err_count);
}
while (1)
{}
}
图6.实验现象
0