一、工具
1、硬件:STM32F103VET6單片機(HAL庫)
2、編譯環境:Atollic TrueSTUDIO for STM32 9.3.0
3、輔助工具:STM32CubeMX
二、電路原理圖
三、單片機系統時鍾配置
1、時鍾源選擇:
2、時鍾樹:
四、SPI配置
1、選用的是SPI1,全雙工主機模式(單片機是主機,外部FLASH做從機),片選引腳由軟件控制。
2、再檢查一下SPI1的引腳設置是否正確。
3、設置FLASH的片選引腳即PC0引腳為輸出模式,因為SPI1上只有一個器件,默認輸出低電平。
五、生成代碼
1、SPI1初始化代碼如下所示:
static void MX_SPI1_Init(void) { /* USER CODE BEGIN SPI1_Init 0 */ /* USER CODE END SPI1_Init 0 */ /* USER CODE BEGIN SPI1_Init 1 */ /* USER CODE END SPI1_Init 1 */ /* SPI1 parameter configuration*/ hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial = 10; if (HAL_SPI_Init(&hspi1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN SPI1_Init 2 */ /* USER CODE END SPI1_Init 2 */ }
2、SPI1引腳及其相關的代碼如下所示:
/** * @brief SPI MSP Initialization * This function configures the hardware resources used in this example * @param hspi: SPI handle pointer * @retval None */ void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(hspi->Instance==SPI1) { /* USER CODE BEGIN SPI1_MspInit 0 */ /* USER CODE END SPI1_MspInit 0 */ /* Peripheral clock enable */ __HAL_RCC_SPI1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**SPI1 GPIO Configuration PA5 ------> SPI1_SCK PA6 ------> SPI1_MISO PA7 ------> SPI1_MOSI */ GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_6; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USER CODE BEGIN SPI1_MspInit 1 */ /* USER CODE END SPI1_MspInit 1 */ } } /** * @brief SPI MSP De-Initialization * This function freeze the hardware resources used in this example * @param hspi: SPI handle pointer * @retval None */ void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi) { if(hspi->Instance==SPI1) { /* USER CODE BEGIN SPI1_MspDeInit 0 */ /* USER CODE END SPI1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_SPI1_CLK_DISABLE(); /**SPI1 GPIO Configuration PA5 ------> SPI1_SCK PA6 ------> SPI1_MISO PA7 ------> SPI1_MOSI */ HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7); /* USER CODE BEGIN SPI1_MspDeInit 1 */ /* USER CODE END SPI1_MspDeInit 1 */ } }
/** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET); /*Configure GPIO pin : PC0 */ GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); }
六、編寫與Flash的相關代碼
1、讀一個字節
/** * @brief SPI1 讀一個字節 * @param None * @retval None */ static uint8_t spi1_flash_read_byte(void) { uint8_t t_data, r_data; if(HAL_SPI_TransmitReceive(&hspi1, &t_data, &r_data, 1, 0xFFFFFF) != HAL_OK) { r_data = 0xff; } return r_data; }
2、寫一個字節
/** * @brief SPI1 寫一個字節 * @param byte 寫入的字節 * @retval 寫狀態 0成功 1失敗 */ static uint8_t spi1_flash_send_byte(uint8_t byte) { uint8_t r_data; if(HAL_SPI_TransmitReceive(&hspi1, &byte, &r_data, 1, 0xFFFFFF) != HAL_OK) { return 1; } return 0; }
3、Flash寫使能
/** * @brief FLASH 寫使能 * @param None * @retval None */ static void spi1_flash_write_enable(void) { cs_low; spi1_flash_send_byte(0x06); cs_high; }
4、Flash等待寫結束
/** * @brief FLASH 等待寫結束 * @param None * @retval None */ static void spi1_flash_wait_for_write_end(void) { uint8_t state = 0; cs_low; spi1_flash_send_byte(0x05); do { state = spi1_flash_read_byte(); } while((state & 0x01) == SET); cs_high; }
5、讀Flash的ID
/** * @brief FLASH 讀ID * @param None * @retval None */ uint32_t spi_flash_read_ID(void) { uint32_t temp, temp0, temp1, temp2; cs_low; spi1_flash_send_byte(0x9F); temp0 = spi1_flash_read_byte(); temp1 = spi1_flash_read_byte(); temp2 = spi1_flash_read_byte(); cs_high; temp = (temp0 << 16) | (temp1 << 8) | temp2; return temp; }
6、讀Flash
/** * @brief 讀FLASH * @param addr 讀flash的起始地址 * @param pdata 讀到的數據存放起始地址 * pdata size 讀數據大小 * @retval None */ void spi1_flash_read(uint32_t addr,uint8_t *pdata, uint16_t size) { cs_low; spi1_flash_send_byte(0x03); spi1_flash_send_byte((addr & 0xFF0000) >> 16); spi1_flash_send_byte((addr & 0xFF00) >> 8); spi1_flash_send_byte(addr & 0xFF); while (size--) { *pdata = spi1_flash_read_byte(); pdata++; } cs_high; }
7、按頁寫Flash
/** * @brief 按頁寫FLASH * @param addr 寫入flash的起始地址 * @param pdata 寫入數據的起始地址 * pdata size 寫數據大小 * @retval None */ void spi1_flash_page_write(uint32_t addr, uint8_t *pdata, uint16_t size) { uint16_t i; spi1_flash_write_enable(); cs_low; spi1_flash_send_byte(0x02); spi1_flash_send_byte((uint8_t)((addr)>>16)); spi1_flash_send_byte((uint8_t)((addr)>>8)); spi1_flash_send_byte((uint8_t)addr); for(i = 0; i < size; i++) { spi1_flash_send_byte(pdata[i]); } cs_high; spi1_flash_wait_for_write_end(); }
8、寫Flash
/** * @brief 寫FLASH * @param addr 寫入flash的起始地址 * @param pdata 寫入數據的起始地址 * pdata size 寫數據大小 * @retval None */ void spi1_flash_write(uint32_t addr, uint8_t *pdata, uint32_t size) { uint32_t page_remain; page_remain = 256 - addr%256; if(size <= page_remain) { page_remain = size; } while(1) { spi1_flash_page_write(addr, pdata, page_remain); if(size == page_remain) break; else { pdata += page_remain; addr += page_remain; size -= page_remain; if(size > 256) page_remain = 256; else page_remain = size; } } }
9、擦除Flash扇區
/** * @brief 擦除FLASH扇區 * @param sector_addr 扇區的起始地址 * @retval None */ void spi1_flash_sector_erase(uint32_t sector_addr) { spi1_flash_write_enable(); spi1_flash_wait_for_write_end(); cs_low; spi1_flash_send_byte(0x20); spi1_flash_send_byte((sector_addr & 0xFF0000) >> 16); spi1_flash_send_byte((sector_addr & 0xFF00) >> 8); spi1_flash_send_byte(sector_addr & 0xFF); cs_high; spi1_flash_wait_for_write_end(); }
10、擦除Flash塊
/** * @brief 擦除FLASH塊 * @param None * @retval None */ void spi1_flash_block_erase(void) { spi1_flash_write_enable(); cs_low; spi1_flash_send_byte(0xC7); cs_high; spi1_flash_wait_for_write_end(); }
#endif