一、问题产生基本情况
一、1 主机
主机F407,时钟频率较高。
SPI具体配置如下:
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
// hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
// hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
// hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_ENABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
// hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCPolynomial = 10;
CUBEMX配置:
PS:由于后期更改都是直接改的代码,并不完全相同(仅供参考)
首先是时钟树如下图。
然后是SPI配置如下图。
一、2 从机
主机F302,时钟频率次之。
SPI具体配置如下:
hspi2.Instance = SPI2;
hspi2.Init.Mode = SPI_MODE_SLAVE;
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.DataSize = SPI_DATASIZE_16BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi2.Init.NSS = SPI_NSS_HARD_INPUT;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 10;
hspi2.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
CUBEMX配置:
PS:由于后期更改都是直接改的代码,并不完全相同(仅供参考)
首先是时钟树如下图。
然后是SPI配置如下图。
二、解决经过
如果您熟悉或了解标题一中的东西,通过上面的一些配置我们对大致情况有了一定的了解,正如我刚接手项目的时候,也是先去了解的参数配置等等。
项目主要是从机的移植,标准库移到HAL库,F030移植到F302,在此之前上一个经手人是移植到了G0上的,但是HAL库中的SPI却没有办法触发中断。
由于项目需求,接收到的数据是要在中断中处理并返回,数据处理函数相当繁琐,这是无可改变的,至少我想不到更好的办法,咱对HAL库也不熟,拿着HAL库的书硬着头皮试呗。
第一版直接卡死不运行,什么问题呢,直接进入HardFault中断里了,里面是while(1)死循环,此时用的是spi中断使能函数和spi使能函数,启动中断,中断回调函数里使用阻塞式接收。
再之后就是中断方式和堵塞式的各种搭配或者单独使用,经常直接卡死收到乱码,于是在Keil中进入的debug模式查句柄中的状态位等等,其中的一些宏定义如下:
errorcode
#define HAL_SPI_ERROR_NONE (0x00000000U) /*!< No error */
#define HAL_SPI_ERROR_MODF (0x00000001U) /*!< MODF error */
#define HAL_SPI_ERROR_CRC (0x00000002U) /*!< CRC error */
#define HAL_SPI_ERROR_OVR (0x00000004U) /*!< OVR error */
#define HAL_SPI_ERROR_FRE (0x00000008U) /*!< FRE error */
#define HAL_SPI_ERROR_DMA (0x00000010U) /*!< DMA transfer error */
#define HAL_SPI_ERROR_FLAG (0x00000020U) /*!< Error on RXNE/TXE/BSY/FTLVL/FRLVL Flag */
#define HAL_SPI_ERROR_ABORT (0x00000040U) /*!< Error during SPI Abort procedure */
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
#define HAL_SPI_ERROR_INVALID_CALLBACK (0x00000080U) /*!< Invalid Callback error */
state
HAL_SPI_STATE_RESET = 0x00U, /*!< Peripheral not Initialized */
HAL_SPI_STATE_READY = 0x01U, /*!< Peripheral Initialized and ready for use */
HAL_SPI_STATE_BUSY = 0x02U, /*!< an internal process is ongoing */
HAL_SPI_STATE_BUSY_TX = 0x03U, /*!< Data Transmission process is ongoing */
HAL_SPI_STATE_BUSY_RX = 0x04U, /*!< Data Reception process is ongoing */
HAL_SPI_STATE_BUSY_TX_RX = 0x05U, /*!< Data Transmission and Reception process is ongoing */
HAL_SPI_STATE_ERROR = 0x06U, /*!< SPI error state */
HAL_SPI_STATE_ABORT = 0x07U /*!< SPI abort is ongoing
@defgroup SPI_Flags_definition SPI Flags Definition
* @{ SR
*/
#define SPI_FLAG_RXNE SPI_SR_RXNE /* SPI status flag: Rx buffer not empty flag */1
#define SPI_FLAG_TXE SPI_SR_TXE /* SPI status flag: Tx buffer empty flag */2
#define SPI_FLAG_BSY SPI_SR_BSY /* SPI status flag: Busy flag */80
#define SPI_FLAG_CRCERR SPI_SR_CRCERR /* SPI Error flag: CRC error flag */
#define SPI_FLAG_MODF SPI_SR_MODF /* SPI Error flag: Mode fault flag */模式故障标志
#define SPI_FLAG_OVR SPI_SR_OVR /* SPI Error flag: Overrun flag */40
#define SPI_FLAG_FRE SPI_SR_FRE /* SPI Error flag: TI mode frame format error flag */
#define SPI_FLAG_FTLVL SPI_SR_FTLVL /* SPI fifo transmission level */1800
#define SPI_FLAG_FRLVL SPI_SR_FRLVL /* SPI fifo reception level */600
#define SPI_FLAG_MASK (SPI_SR_RXNE | SPI_SR_TXE | SPI_SR_BSY | SPI_SR_CRCERR\
| SPI_SR_MODF | SPI_SR_OVR | SPI_SR_FRE | SPI_SR_FTLVL | SPI_SR_FRLVL)
经常出现的就是溢出,电平,模式故障标志,到最后写了错误中断回调处理函数,倒是可以一直运行了,只是接收到的数据都不对,最后经查证是每次都溢出了,可为啥之前F302标准库就可以呢,同样都是复杂的中断处理函数,甚至现在的时钟还比原来高,随后开始研究HAL库一次完整的中断都要干什么,最后发现那干的可太多了比我处理函数都复杂,甚至于回调函数啥也不干就光储存数据都经常溢出。
其实早就想用DMA了,主要是对DMA不太熟悉,对HAL库也不太熟,并且时间比较紧,谁知道能整出啥幺蛾子。
于是不得已尝试了,心中很早就想试的方法了,直接删掉自带中断服务函数,直接调用我的回调函数,直接读寄存器,经过对HAL库中断函数的研究,加上点需要的东西就可以了。于是结果就确实可以了。
三、代码分享
首先、主函数里先开启中断:
__HAL_SPI_ENABLE_IT(&hspi2, SPI_IT_RXNE);
__HAL_SPI_ENABLE(&hspi2);
然后我们看中断函数:
void SPI2_IRQHandler(void)
{
/* USER CODE BEGIN SPI2_IRQn 0 */
/* USER CODE END SPI2_IRQn 0 */
HAL_SPI_TxRxCpltCallback(&hspi2);
/* USER CODE BEGIN SPI2_IRQn 1 */
/* USER CODE END SPI2_IRQn 1 */
}
就一个回调没别的。
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
if(hspi ==(&hspi2))
{
if(SPI_RxCnt==0) memset(SPI_RxBuff,0,sizeof(SPI_RxBuff));
SPI_RxBuff[SPI_RxCnt]=SPI2_ReadByte16(&hspi2);
SPI_RxCnt++;
data_processing();//数据处理
}
}
数据处理函数中主要用到了以下两个函数:
void SPI2_WriteByte16(uint16_t TxData)
{
while(__HAL_SPI_GET_FLAG(&hspi2,SPI_FLAG_TXE)==false);
// SPI_TxBuff[0]=TxData;
hspi2.Instance->DR = TxData;
}
uint16_t SPI2_ReadByte16(SPI_HandleTypeDef *hspi)
{
uint16_t res = 0;
while(__HAL_SPI_GET_FLAG(&hspi2,SPI_FLAG_RXNE)==false);
*((uint16_t *)&res) = (uint16_t)hspi->Instance->DR;
return res;
}
如果使用HAL库函数发送,发送完一次会自动关闭中断,需要再次使用函数。但效率堪忧,直接从中提取用于开启中断的函数使用,在使用HAL库函数发送时又会引起错误,第一版就是这种情况。
所以最后这个硬解法,其实什么也不用额外加。
PS:本人水平有限,此方法针对不同项目需求有效性可能并不一致,不过确实是能收到正确的数据了,正确率吗还是有的,毕竟应用层协议和如何主机如何发送,压根就不是我写的(是之前就做好的),还用的是HAL库收发,他是主机他肯定是想咋样咋样,我这是从机。能力属实有限,还请各位看官多多指教。