以STM32裸機開發為例。
軟件分層 |
---|
應用層 |
驅動層 |
硬件層 |
固件層 |
①最底層為固件層,Firmware
這一層通常是官方給的庫,庫函數對寄存器進行操作,例如:
/*** @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;
}
②往上一層為硬件層,Hardware。
這一層的函數是在庫函數的基礎上進一步封裝,比如根據STM32 SPI的特性,SPI讀寫一個數據我們封裝為一個函數,例如:
u8 SPI2_ReadWriteByte(u8 TxData)
{ u8 retry = 0; while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET){retry++;if(retry > 200)return 0;} SPI_I2S_SendData(SPI2, TxData); retry = 0;while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) {retry++;if(retry > 200)return 0;} return SPI_I2S_ReceiveData(SPI2);
}
這個函數的也只讀寫送一個數據,我們通常不會只讀寫一個數據,而是連續讀寫送N個數據,所以在這個函數的基礎上再封裝出一個函數,例如:
void SPI2_ReadWriteData(u8 *sendData, u8 *recvData, u32 length)
{u32 i = 0;for(i = 0; i < length; i++){recvData[i] = SPI2_ReadWriteByte(sendData[i]);}
}
③再往上一層是驅動層,drive。
前面兩層都是對STM32進行操作,而這一層是對與STM32連接的元器件、模塊、模組進行操作,通常稱為調試驅動,比如調試STM32和外部FLASH W25Q128的SPI驅動。
這一層的函數通常是對W25Q128進行操作,例如:
u16 W25QXX_ReadID(void)
{u16 Temp = 0; W25QXX_CS = 0; SPI2_ReadWriteByte(0x90); SPI2_ReadWriteByte(0x00); SPI2_ReadWriteByte(0x00); SPI2_ReadWriteByte(0x00); Temp |= SPI2_ReadWriteByte(0xFF) << 8; Temp |= SPI2_ReadWriteByte(0xFF); W25QXX_CS = 1; return Temp;
}
④再往上一層是應用層,APP。
有很多人往往在main.c里面做功能應用,導致main.c內容又多又長,main函數又長又臭,大多數公司的代碼規范要求是不允許的。最好把功能應用剝離出來,單獨放在應用層,讓main函數簡潔清晰。
例如:
int main(void)
{ STM32_Sys_Init();W25Q128_Init();//如果這些初始化超過3個,就要考慮封裝成一個函數UART_Init();//如果這些初始化超過3個,就要考慮封裝成一個函數while(1){switch(g_main_process){case MAIN_UART_RECV_HANDLE://APP_func1();break;case MAIN_XXX_HANDLE://APP_func2();break;//.............default://APP_funcN();break;}}
}
總結:
用分層思想不僅提高代碼閱讀性(方便新員工接手辭職員工代碼,而不是一團糟讓人一頭霧水),還提高可移植性,如果項目需要換一顆MCU,替換固件層,替換硬件層幾個庫函數即可,其他層不需要動。