从M3到M0,可能SPI的接口函数大致类似,但是细节略有不同

仔细观察寄存器描述,虽然个别存在差异,但是真心不知道竟然有太多的“玄机”

这次的问题主要出在了数据宽度上:

1. M3/M4的数据宽度支持8/16,是SPI_CR1中DFF: Data frame format控制的,实际使用中,只要我配置好数据宽度,直接操作DR寄存器即可。

2. M0的看起来更加强大,在SPI_CR2中DS [3:0]: Data size控制,支持4..16个bits的数据。

所以开始的时候我使用M3的操作方式,直接把驱动函数移了过来,下面为初始化代码

 /*
*********************************************************************************************************
* Function Name: bsp_spi_init
* Description : SPI初始化
* Input : None
* Output : None
* Return : None
*********************************************************************************************************
*/
static void
bsp_spi_init(void)
{
SPI_InitTypeDef SPI_InitStructure; BSP_SPI_FRAM_CS(); /* SPI_CS初始化 */
SPI_StructInit(&SPI_InitStructure);
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
SPI_Init(BSP_SPI_FRAM, &SPI_InitStructure);
/** Enable the SPI */
SPI_Cmd(BSP_SPI_FRAM, ENABLE);
}

本以为这样就OK了,后来发现二者还是有区别的:

M3的话你直接写DR寄存器,会自动根据你的配置数据长度8/16发送数据

M0的话,你直接写DR寄存器,他不会根据你写的数据长度发送,依旧发送的是16bits

后来观察ST官方给的驱动代码:

M3的接口函数没有区分,都是直接操作DR

// M3的发送代码
/**
* @brief Transmits a Data through the SPIx/I2Sx peripheral.
* @param SPIx: where x can be
* - 1, 2 or 3 in SPI mode
* - 2 or 3 in I2S mode
* @param Data : Data to be transmitted.
* @retval None
*/
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx)); /* Write in the DR register the data to be sent */
SPIx->DR = Data;
}

M0的官方代码比较复杂:

/**
* @brief Transmits a Data through the SPIx/I2Sx peripheral.
* @param SPIx: where x can be 1 or 2 in SPI mode to select the SPI peripheral.
* @param Data: Data to be transmitted.
* @retval None
*/
void SPI_SendData8(SPI_TypeDef* SPIx, uint8_t Data)
{
uint32_t spixbase = 0x00; /* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx)); spixbase = (uint32_t)SPIx;
spixbase += 0x0C; *(__IO uint8_t *) spixbase = Data;
} /**
* @brief Transmits a Data through the SPIx/I2Sx peripheral.
* @param SPIx: where x can be 1 or 2 in SPI mode or 1 in I2S mode to select
* the SPI peripheral.
* @param Data: Data to be transmitted.
* @retval None
*/
void SPI_I2S_SendData16(SPI_TypeDef* SPIx, uint16_t Data)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx)); SPIx->DR = (uint16_t)Data;
}

虽然都是操作的DR,但是M0这里把DR强转成8bits宽度的指针,之后操作。

因为我只是发送8bits的数据,所以这样算是OK了

至于其他的非8/16的宽度如何操作,暂时就不去纠结了。。。。

我最后的做法简单粗暴,直接对DR强行转换:

         (*((volatile unsigned char*)(&BSP_SPI_FRAM->DR) ))= dat;

这样免得调用他那个发送的库,各个平台的代码归档起来也方便一些。

至于这样设计的原因,我考虑还是M0的字节对齐问题吧?

05-15 20:55