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的字节对齐问题吧?

posted @ 2018-08-17 11:40  linux_farmer  阅读(440)  评论(0编辑  收藏  举报