STM32 M0之SPI
从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的操作方式,直接把驱动函数移了过来,下面为初始化代码
1 /* 2 ********************************************************************************************************* 3 * Function Name: bsp_spi_init 4 * Description : SPI初始化 5 * Input : None 6 * Output : None 7 * Return : None 8 ********************************************************************************************************* 9 */ 10 static void 11 bsp_spi_init(void) 12 { 13 SPI_InitTypeDef SPI_InitStructure; 14 15 BSP_SPI_FRAM_CS(1); /* SPI_CS初始化 */ 16 SPI_StructInit(&SPI_InitStructure); 17 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; 18 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; 19 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; 20 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; 21 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; 22 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB; 23 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; 24 SPI_Init(BSP_SPI_FRAM, &SPI_InitStructure); 25 /** Enable the SPI */ 26 SPI_Cmd(BSP_SPI_FRAM, ENABLE); 27 }
本以为这样就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强行转换:
1 (*((volatile unsigned char*)(&BSP_SPI_FRAM->DR) ))= dat;
这样免得调用他那个发送的库,各个平台的代码归档起来也方便一些。
至于这样设计的原因,我考虑还是M0的字节对齐问题吧?
博客园:http://www.cnblogs.com/linux-farmer/