
みなさんこんにちは! この記事では、stm32シリーズプラットフォーム用のドライバーを作成した経験を共有したいと思います。 Emboxでは、STM32F3、STM32F4などの一連のプラットフォームごとにドライバーを作成する必要はありません。 実際、時間がかかるという事実に加えて、新しいコードには必然的に新しいエラーが含まれます。
最初に、さまざまなデバイスの例を取り上げ、ニーズに合わせて変更しました。 しかし、私はまだいくつかの共通のベースを使用したかったので、そのようなベースとして、マイクロコントローラーデータプロデューサー(STMicroelectronics)は
stm32cubeライブラリ(より正確には、一連のライブラリ)を提供します。 残念ながら、これらのライブラリにはわずかに異なるものが含まれていますが、異なるシリーズのプラットフォーム用のAPIは非常に似ています。 実際、多くの人が知っているように、stm32cubeにはコードジェネレーターであるstm32cubemxが含まれています。 つまり、その助けを借りて、このシリーズから任意のプラットフォームのプロジェクトフレームワークを生成することができ、開発者は必要な機能を既に追加できます。 このため、単一のインターフェースを実際に気にすることはできません。
しかし、私が言ったように、stm32cubeを使用して、統一されたAPIでドライバーを作成したいと考えました。これにより、デバイスレベルに依存せずに高レベルのソフトウェアを使用できるようになります。 さらに、すでに
記事の 1つで書いたように、現代の小型ARM(cortex-m)デバイスの特性は、暫定的にペンティアムに近づいています。 もちろん、それらをクールなarduinoとして使い続けることはできますが、その可能性を完全に明らかにすることはできません。
起動コードとリンカースクリプト
通常、コントローラーとプロセッサーから最初に開始することは、ブートコード、割り込みベクター、およびその他のアーキテクチャ機能です。 これがstm32cubeでどのように構成されているかを簡単に説明します。
stm32cubeには、各コントローラーモデル、さらには各コンパイラー(ARM、AIR、およびgcc)に対しても特定の開始コードがあります。 これはすべて、Drivers / CMSIS / Device / ST / STM32ディレクトリにあります。XXXは、F3、F4、F7などのシリーズです。
開始コード自体は、各コントローラシリーズの個別のアセンブラファイルに含まれており、プロジェクトの構成時に接続されます。 ファイルのgcc-shnyバージョンに近づいています。 これらは、サブフォルダーSource / Templates / gccにあります。
一対のアセンブラファイルを比較すると、それらには同じブートコードが含まれていることがわかりました。このブートコードは、割り込みのテーブル(ベクトル)のみが異なります。 コントローラーのモデルごとにハードウェアデバイスのセットが異なるため、この違いは非常に予想されます。 しかし、私の意見では、すべてのコントローラーの共通コードを別のファイルに入れ、コントローラーでサポートされている機器に基づいてさまざまな割り込みテーブルを生成すると、はるかに良くなります。 これが、Emboxで行ったことです。
開始コードに加えて、一連の各コントローラーのリンカースクリプトは同じフォルダーにあります。 繰り返しますが、それらを見て比較すると、それらがどれほど似ているかがわかります。 セクションのサイズとCCMRAMセクションの存在のみで見つかった違い。 このプロジェクトでは、リンカスクリプトの重複を取り除き、設定ファイルを取得し、プリプロセッサの助けを借りてそれをリンカスクリプトに変換する小さなジェネレータを作成しました。
たとえば、stm32f3-discoveryの構成ファイルを作成する方法は次のとおりです。/* region (origin, length) */
ROM (0x08000000, 256K)
RAM (0x20000000, 40K)
region(SRAM_CCM, 0x10000000, 8K)
/* section (region[, lma_region]) */
text (ROM)
rodata (ROM)
data (RAM, ROM)
bss (RAM)
デバッグインターフェース
原則として、ブートコードとリンカースクリプトは、プログラムを実行するのに十分です。その後、デバッガーに接続して手順を実行できます。 しかし、これは有用な作業を行うには絶対に十分ではありません。 プログラムは、外部の影響に対応するか、少なくとも何らかの形でそれ自体を明示しなければなりません。 最も単純な症状は、点滅するLED、またはUART(COMポート)への何かの出力です。 私たちのプロジェクトでは、UARTに基づいたdiagインターフェースから開始するのが慣例です。これにより、以降のデバッグが大幅に簡素化されます。
UARTインターフェイス自体と同様に、シリアルポートの実装は非常に簡単です。 const struct uart_ops stm32_uart_ops = { .uart_getc = stm32_uart_getc, .uart_putc = stm32_uart_putc, .uart_hasrx = stm32_uart_hasrx, .uart_setup = stm32_uart_setup, }; static struct uart stm32_diag = { .uart_ops = &stm32_uart_ops, .irq_num = USARTx_IRQn, .base_addr = (unsigned long) USARTx, };
実際、4つの関数を実装するだけで十分です。そのうち3つは簡単なもので、特定の制御レジスタの記録または読み取りです。
uart_setup()
関数はそれほど複雑ではなく、ポートパラメータを設定します。
その結果、STM32CUBEのAPIを使用したコードは次のとおりです。 static int stm32_uart_setup(struct uart *dev, const struct uart_params *params) { UART_HandleTypeDef UartHandle; memset(&UartHandle, 0, sizeof(UartHandle)); UartHandle.Instance = (void*) dev->base_addr; UartHandle.Init.BaudRate = params->baud_rate; UartHandle.Init.WordLength = UART_WORDLENGTH_8B; UartHandle.Init.StopBits = UART_STOPBITS_1; UartHandle.Init.Parity = UART_PARITY_NONE; UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE; UartHandle.Init.Mode = UART_MODE_TX_RX; if (HAL_UART_Init(&UartHandle) != HAL_OK) { return -1; } if (dev->params.irq) { __HAL_UART_ENABLE_IT(&UartHandle, UART_IT_RXNE); } return 0; } static int stm32_uart_putc(struct uart *dev, int ch) { USART_TypeDef *uart = (void *) dev->base_addr; while ((STM32_USART_FLAGS(uart) & USART_FLAG_TXE) == 0); STM32_USART_TXDATA(uart) = (uint8_t) ch; return 0; } static int stm32_uart_hasrx(struct uart *dev) { USART_TypeDef *uart = (void *) dev->base_addr; return STM32_USART_FLAGS(uart) & USART_FLAG_RXNE; } static int stm32_uart_getc(struct uart *dev) { USART_TypeDef *uart = (void *) dev->base_addr; return (uint8_t)(STM32_USART_RXDATA(uart) & 0xFF); }
UARTがSTM32Cubeで動作するには、1つまたは別のファミリーのコントローラーに
HAL_UART_MspInit()
を実装する必要があります。 この関数はキューブ自体で弱いと宣言され、特定の例ではオーバーライドされます。
HAL_UART_Init()
から
HAL_UART_Init()
れ、実際には、必要に応じてI / Oピンを構成します。
stm32f4の場合、コードは次のようになります void HAL_UART_MspInit(UART_HandleTypeDef *huart) { GPIO_InitTypeDef GPIO_InitStruct; void *uart_base = huart->Instance; USART_TX_GPIO_CLK_ENABLE(uart_base); USART_RX_GPIO_CLK_ENABLE(uart_base); USART_CLK_ENABLE(uart_base); GPIO_InitStruct.Pin = USART_TX_PIN(uart_base); GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = USART_TX_AF(uart_base); HAL_GPIO_Init(USART_TX_GPIO_PORT(uart_base), &GPIO_InitStruct); GPIO_InitStruct.Pin = USART_RX_PIN(uart_base); GPIO_InitStruct.Alternate = USART_RX_AF(uart_base); HAL_GPIO_Init(USART_RX_GPIO_PORT(uart_base), &GPIO_InitStruct); }
Embox OSでは、UNIXインターフェースを介して複数のUARTを使用できます。つまり、ファイル名(/ dev / ttyS0、/ dev / ttyS1)でデバイスにアクセスできます。 stm32のこの機能をサポートするために、各シリーズのヘッダーファイルを作成するよりも優れたものはまだありません。その中で、必要なパラメーターを決定できます。異なるシリーズは異なります。
STM32F4の場合、次のようになります #define MODOPS_USARTX OPTION_GET(NUMBER, usartx) #if MODOPS_USARTX == 6 #define USARTx USART6 #define USARTx_CLK_ENABLE() __HAL_RCC_USART6_CLK_ENABLE() #define USARTx_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() #define USARTx_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() #define USARTx_FORCE_RESET() __HAL_RCC_USART6_FORCE_RESET() #define USARTx_RELEASE_RESET() __HAL_RCC_USART6_RELEASE_RESET() #define USARTx_TX_PIN GPIO_PIN_6 #define USARTx_TX_GPIO_PORT GPIOC #define USARTx_TX_AF GPIO_AF8_USART6 #define USARTx_RX_PIN GPIO_PIN_7 #define USARTx_RX_GPIO_PORT GPIOC #define USARTx_RX_AF GPIO_AF8_USART6 #define USARTx_IRQn USART6_IRQn + 16 #define USARTx_IRQHandler USART6_IRQHandler #elif MODOPS_USARTX == 2 #define USARTx USART2 #define USARTx_CLK_ENABLE() __HAL_RCC_USART2_CLK_ENABLE() #define USARTx_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define USARTx_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define USARTx_FORCE_RESET() __HAL_RCC_USART2_FORCE_RESET() #define USARTx_RELEASE_RESET() __HAL_RCC_USART2_RELEASE_RESET() #define USARTx_TX_PIN GPIO_PIN_2 #define USARTx_TX_GPIO_PORT GPIOA #define USARTx_TX_AF GPIO_AF7_USART2 #define USARTx_RX_PIN GPIO_PIN_3 #define USARTx_RX_GPIO_PORT GPIOA #define USARTx_RX_AF GPIO_AF7_USART2 #define USARTx_IRQn USART2_IRQn + 16 #define USARTx_IRQHandler USART2_IRQHandler #else #error Unsupported USARTx #endif #define STM32_USART_FLAGS(uart) uart->SR #define STM32_USART_RXDATA(uart) uart->DR #define STM32_USART_TXDATA(uart) uart->DR
STM32F7の場合 #define MODOPS_USARTX OPTION_GET(NUMBER, usartx) #if MODOPS_USARTX == 6 #define USARTx USART6 #define USARTx_CLK_ENABLE() __HAL_RCC_USART6_CLK_ENABLE(); #define USARTx_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() #define USARTx_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() #define USARTx_FORCE_RESET() __HAL_RCC_USART6_FORCE_RESET() #define USARTx_RELEASE_RESET() __HAL_RCC_USART6_RELEASE_RESET() #define USARTx_TX_PIN GPIO_PIN_6 #define USARTx_TX_GPIO_PORT GPIOC #define USARTx_TX_AF GPIO_AF8_USART6 #define USARTx_RX_PIN GPIO_PIN_7 #define USARTx_RX_GPIO_PORT GPIOC #define USARTx_RX_AF GPIO_AF8_USART6 #define USARTx_IRQn USART6_IRQn + 16 #define USARTx_IRQHandler USART6_IRQHandler #elif MODOPS_USARTX == 2 #define USARTx USART2 #define USARTx_CLK_ENABLE() __HAL_RCC_USART2_CLK_ENABLE(); #define USARTx_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define USARTx_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define USARTx_FORCE_RESET() __HAL_RCC_USART2_FORCE_RESET() #define USARTx_RELEASE_RESET() __HAL_RCC_USART2_RELEASE_RESET() #define USARTx_TX_PIN GPIO_PIN_2 #define USARTx_TX_GPIO_PORT GPIOA #define USARTx_TX_AF GPIO_AF7_USART2 #define USARTx_RX_PIN GPIO_PIN_3 #define USARTx_RX_GPIO_PORT GPIOA #define USARTx_RX_AF GPIO_AF7_USART2 #define USARTx_IRQn USART2_IRQn + 16 #define USARTx_IRQHandler USART2_IRQHandler #elif MODOPS_USARTX == 1 #define USARTx USART1 #define USARTx_CLK_ENABLE() __HAL_RCC_USART1_CLK_ENABLE(); #define USARTx_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() #define USARTx_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define USARTx_FORCE_RESET() __HAL_RCC_USART1_FORCE_RESET() #define USARTx_RELEASE_RESET() __HAL_RCC_USART1_RELEASE_RESET() #define USARTx_TX_PIN GPIO_PIN_9 #define USARTx_TX_GPIO_PORT GPIOA #define USARTx_TX_AF GPIO_AF7_USART1 #define USARTx_RX_PIN GPIO_PIN_7 #define USARTx_RX_GPIO_PORT GPIOB #define USARTx_RX_AF GPIO_AF7_USART1 #define USARTx_IRQn USART1_IRQn + 16 #define USARTx_IRQHandler USART1_IRQHandler #else #error Unsupported USARTx #endif #define STM32_USART_FLAGS(uart) uart->ISR #define STM32_USART_RXDATA(uart) uart->RDR #define STM32_USART_TXDATA(uart) uart->TDR
STM32F3の場合 #define MODOPS_USARTX OPTION_GET(NUMBER, usartx) #if MODOPS_USARTX == 1 #define USARTx USART1 #define USARTx_CLK_ENABLE() __USART1_CLK_ENABLE(); #define USARTx_RX_GPIO_CLK_ENABLE() __GPIOC_CLK_ENABLE() #define USARTx_TX_GPIO_CLK_ENABLE() __GPIOC_CLK_ENABLE() #define USARTx_FORCE_RESET() __USART1_FORCE_RESET() #define USARTx_RELEASE_RESET() __USART1_RELEASE_RESET() #define USARTx_TX_PIN GPIO_PIN_4 #define USARTx_TX_GPIO_PORT GPIOC #define USARTx_TX_AF GPIO_AF7_USART1 #define USARTx_RX_PIN GPIO_PIN_5 #define USARTx_RX_GPIO_PORT GPIOC #define USARTx_RX_AF GPIO_AF7_USART1 #define USARTx_IRQn USART1_IRQn + 16 #define USARTx_IRQHandler USART1_IRQHandler #elif MODOPS_USARTX == 2 #define USARTx USART2 #define USARTx_CLK_ENABLE() __USART2_CLK_ENABLE(); #define USARTx_RX_GPIO_CLK_ENABLE() __GPIOA_CLK_ENABLE() #define USARTx_TX_GPIO_CLK_ENABLE() __GPIOA_CLK_ENABLE() #define USARTx_FORCE_RESET() __USART2_FORCE_RESET() #define USARTx_RELEASE_RESET() __USART2_RELEASE_RESET() #define USARTx_TX_PIN GPIO_PIN_2 #define USARTx_TX_GPIO_PORT GPIOA #define USARTx_TX_AF GPIO_AF7_USART2 #define USARTx_RX_PIN GPIO_PIN_3 #define USARTx_RX_GPIO_PORT GPIOA #define USARTx_RX_AF GPIO_AF7_USART2 #define USARTx_IRQn USART2_IRQn + 16 #define USARTx_IRQHandler USART2_IRQHandler #elif MODOPS_USARTX == 3 #define USARTx USART3 #define USARTx_CLK_ENABLE() __USART3_CLK_ENABLE(); #define USARTx_RX_GPIO_CLK_ENABLE() __GPIOB_CLK_ENABLE() #define USARTx_TX_GPIO_CLK_ENABLE() __GPIOB_CLK_ENABLE() #define USARTx_FORCE_RESET() __USART3_FORCE_RESET() #define USARTx_RELEASE_RESET() __USART3_RELEASE_RESET() #define USARTx_TX_PIN GPIO_PIN_10 #define USARTx_TX_GPIO_PORT GPIOB #define USARTx_TX_AF GPIO_AF7_USART3 #define USARTx_RX_PIN GPIO_PIN_11 #define USARTx_RX_GPIO_PORT GPIOB #define USARTx_RX_AF GPIO_AF7_USART3 #define USARTx_IRQn USART3_IRQn + 16 #define USARTx_IRQHandler USART3_IRQHandler #endif #define STM32_USART_FLAGS(uart) uart->ISR #define STM32_USART_RXDATA(uart) uart->RDR #define STM32_USART_TXDATA(uart) uart->TDR
上記のコードを見ると、チェックしたボードのUART番号のペアでのみサポートされているものが表示されます。 一方、タスクは完了しました; Emboxが起動すると、デバイスで/ dev / ttyS0と/ dev / ttyS1が発生し、構成で指定された番号でUARTのハードウェアポートに関連付けられました。
構成例@Runlevel(1) include embox.driver.serial.stm_ttyS1(baud_rate=57600, usartx=2)
@Runlevel(1) include embox.driver.serial.stm_ttyS0(baud_rate=115200, usartx=6)
タイマーと割り込みコントローラー
移植の次のステップは、通常、タイマーと割り込みコントローラーをサポートすることです。 これらのデバイスのドライバーは、別のライブラリ
-CMSIS (Cortex Microcontroller Software Interface Standard)に基づいています。 すでに持っていて、やり直す必要はありませんでした。
uint32_t HAL_GetTick(void)
機能するためには、uint32_t
uint32_t HAL_GetTick(void)
関数を実装しなければならなかったことを明確にし、初期化ではチートする必要がありました。
すべてのプラットフォームの最小初期化は非常に似ています。 実際、
HAL_Init()
から
SystemInit()
および
HAL_Init()
を呼び出し、特定のプラットフォーム用にブロックを構成する必要があります。
stm32f4の場合 static void SystemClock_Config(void) { RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_OscInitTypeDef RCC_OscInitStruct; __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 8; RCC_OscInitStruct.PLL.PLLN = 336; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 7; HAL_RCC_OscConfig(&RCC_OscInitStruct); RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5); if (HAL_GetREVID() == 0x1001) { __HAL_FLASH_PREFETCH_BUFFER_ENABLE(); } }
stm32f7の場合 static void SystemClock_Config(void) { RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_OscInitTypeDef RCC_OscInitStruct; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSIState = RCC_HSI_OFF; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 25; RCC_OscInitStruct.PLL.PLLN = 432; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 9; if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { printf(">>> SystemClock_Config failed\n"); } if(HAL_PWREx_EnableOverDrive() != HAL_OK) { printf(">>> SystemClock_Config failed\n"); } RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK) { printf(">>> SystemClock_Config failed\n"); } }
stm32f3の場合 static void SystemClock_Config(void) { RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_OscInitTypeDef RCC_OscInitStruct; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK) {
実際には、その後システムで割り込みとタイマーが機能し、これはプリエンプティブマルチタスクを整理するのに十分です。 これらのマイクロコントローラーでマルチタスクのパワーを感じるためには、httpサーバーとtelnetサーバーを実行するだけで十分ですが、そのためにはネットワークカードドライバーを実装する必要があります。 ここでは、共通のドライバーがあり、レッグなどを設定する特定のプラットフォーム用に個別の構成があるという意味で、UARTと似ています。
イーサネット
構成から始めましょう。 stm32f3にはイーサネットコントローラーがないため、stm32f4およびstm32f7のみのバージョンがあります。
stm32f4の場合 void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) { GPIO_InitTypeDef GPIO_InitStructure; __HAL_RCC_ETH_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; GPIO_InitStructure.Pull = GPIO_NOPULL; GPIO_InitStructure.Alternate = GPIO_AF11_ETH; HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.Pin = GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13; HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); if (heth->Init.MediaInterface == ETH_MEDIA_INTERFACE_MII) { HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1); } GPIO_InitStructure.Pin = GPIO_PIN_2; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.Pull = GPIO_PULLUP; GPIO_InitStructure.Speed = GPIO_SPEED_FAST; HAL_GPIO_Init(GPIOE, &GPIO_InitStructure); HAL_GPIO_WritePin(GPIOE, GPIO_PIN_2, GPIO_PIN_RESET); HAL_Delay(1); HAL_GPIO_WritePin(GPIOE, GPIO_PIN_2, GPIO_PIN_SET); HAL_Delay(1); }
stm32f7の場合 void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) { GPIO_InitTypeDef GPIO_InitStructure; __HAL_RCC_GPIOA_CLK_ENABLE() ; __HAL_RCC_GPIOC_CLK_ENABLE() ; __HAL_RCC_GPIOG_CLK_ENABLE(); GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; GPIO_InitStructure.Pull = GPIO_NOPULL; GPIO_InitStructure.Alternate = GPIO_AF11_ETH; GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7; HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.Pin = GPIO_PIN_2 | GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14; HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); HAL_NVIC_EnableIRQ(ETH_IRQn); __HAL_RCC_ETH_CLK_ENABLE(); }
実際、イーサネットコントローラーの初期化コード用のある種のジェネレーターはそれ自体を示唆していますが、これまでのところ手が届きません。
Emboxにネットワークデバイスを実装するには、struct net_driver構造体のいくつかのフィールドに入力するだけです。
static const struct net_driver stm32eth_ops = { .xmit = stm32eth_xmit, .start = stm32eth_open, .set_macaddr = stm32eth_set_mac, };
ドライバーモデルはLinuxに似ていますが、インターフェース上で完全に一致する場合もあります。 したがって、Linuxに非常によく似たコードが表示されても驚かないでください。これは、ドライバーの開発を簡素化するために意図的に行われたものです。
初期化 static int stm32eth_init(void) { int res; struct net_device *nic; nic = (struct net_device *) etherdev_alloc(0); if (nic == NULL) { return -ENOMEM; } nic->drv_ops = &stm32eth_ops; nic->irq = STM32ETH_IRQ; nic->base_addr = ETH_BASE; nic_priv = netdev_priv(nic, struct stm32eth_priv); stm32eth_netdev = nic; res = irq_attach(nic->irq, stm32eth_interrupt, 0, stm32eth_netdev, ""); if (res < 0) { return res; } return inetdev_register_dev(nic); }
さらに、ライブラリ自体の初期化が必要です。 static void low_level_init(unsigned char mac[6]) {
送信 static int stm32eth_xmit(struct net_device *dev, struct sk_buff *skb) { __IO ETH_DMADescTypeDef *dma_tx_desc; dma_tx_desc = stm32_eth_handler.TxDesc; memcpy((void *)dma_tx_desc->Buffer1Addr, skb->mac.raw, skb->len); HAL_ETH_TransmitFrame(&stm32_eth_handler, skb->len); skb_free(skb); return 0; }
送信のために、パケットにいくつかのバッファを割り当て、送信用のパケットを受信すると、データを既に選択されている領域にコピーしてから、STM32CUBEから送信関数を呼び出します。割り込みによりパケットを受信する: static irq_return_t stm32eth_interrupt(unsigned int irq_num, void *dev_id) { struct net_device *nic_p = dev_id; struct sk_buff *skb; ETH_HandleTypeDef *heth = &stm32_eth_handler; if (!nic_p) { return IRQ_NONE; } if (__HAL_ETH_DMA_GET_FLAG(heth, ETH_DMA_FLAG_R)) { while (NULL != (skb = low_level_input())) { skb->dev = nic_p; show_packet(skb->mac.raw, skb->len, "rx"); netif_rx(skb); } __HAL_ETH_DMA_CLEAR_IT(heth, ETH_DMA_IT_R); } __HAL_ETH_DMA_CLEAR_IT(heth, ETH_DMA_IT_NIS); return IRQ_HANDLED; }
受信機能コード static struct sk_buff *low_level_input(void) { struct sk_buff *skb; int len; uint8_t *buffer; uint32_t i=0; __IO ETH_DMADescTypeDef *dmarxdesc; skb = NULL; if (HAL_ETH_GetReceivedFrame_IT(&stm32_eth_handler) != HAL_OK) return NULL; len = stm32_eth_handler.RxFrameInfos.length; buffer = (uint8_t *) stm32_eth_handler.RxFrameInfos.buffer; skb = skb_alloc(len); if (skb != NULL) { memcpy(skb->mac.raw, buffer, len); } dmarxdesc = stm32_eth_handler.RxFrameInfos.FSRxDesc; for (i=0; i< stm32_eth_handler.RxFrameInfos.SegCount; i++) { dmarxdesc->Status |= ETH_DMARXDESC_OWN; dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr); } stm32_eth_handler.RxFrameInfos.SegCount =0; if ((stm32_eth_handler.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET) { stm32_eth_handler.Instance->DMASR = ETH_DMASR_RBUS; stm32_eth_handler.Instance->DMARPDR = 0; } return skb; }
実際、これがドライバー全体です。実装後、Emboxにあるすべてのネットワークユーティリティが動作します。アプリケーションについては、以前の記事(pjsipおよびhttpd)で読むことができ、リストされているstm32cubeベースのドライバーに加えて、spi、i2c、lcd、アクセラレーター、ジャイロスコープなども実装されています。PS私たちのレポートはOSDAY(モスクワで5月23日〜24日)で受け入れられました。プロジェクトについてライブで聞くことに興味があるなら、大歓迎です。