stm32cubeライブラリを使用して、プラットフォームに依存しないドライバーを作成する

みなさんこんにちは! この記事では、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) { /* Enable the UART Data Register not empty Interrupt */ __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; /*##-1- Enable peripherals and GPIO Clocks #################################*/ /* Enable GPIO TX/RX clock */ USART_TX_GPIO_CLK_ENABLE(uart_base); USART_RX_GPIO_CLK_ENABLE(uart_base); /* Enable USART2 clock */ USART_CLK_ENABLE(uart_base); /*##-2- Configure peripheral GPIO ##########################################*/ /* UART TX GPIO pin configuration */ 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); /* UART RX GPIO pin configuration */ 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() /* Definition for USARTx Pins */ #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 /* Definition for USARTx's NVIC */ #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() /* Definition for USARTx Pins */ #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 /* Definition for USARTx's NVIC */ #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() /* Definition for USARTx Pins */ #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 /* Definition for USARTx's NVIC */ #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() /* Definition for USARTx Pins */ #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 /* Definition for USARTx's NVIC */ #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() /* Definition for USARTx Pins */ #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 /* Definition for USARTx's NVIC */ #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() /* Definition for USARTx Pins */ #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 /* Definition for USARTx's NVIC */ /* In Embox we assume that the lower external irq number is 0, * but in the cortexm3 it is -15 */ #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() /* Definition for USARTx Pins */ #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 /* Definition for USARTx's NVIC */ /* In Embox we assume that the lower external irq number is 0, * but in the cortexm3 it is -15 */ #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() /* Definition for USARTx Pins */ #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 /* Definition for USARTx's NVIC */ /* In Embox we assume that the lower external irq number is 0, * but in the cortexm3 it is -15 */ #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; /* Enable Power Control clock */ __HAL_RCC_PWR_CLK_ENABLE(); /* The voltage scaling allows optimizing the power consumption when the device is clocked below the maximum system frequency, to update the voltage scaling value regarding system frequency refer to product datasheet. */ __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /* Enable HSE Oscillator and activate PLL with HSE as source */ 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); /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */ 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); /* STM32F405x/407x/415x/417x Revision Z devices: prefetch is supported */ if (HAL_GetREVID() == 0x1001) { /* Enable the Flash prefetch */ __HAL_FLASH_PREFETCH_BUFFER_ENABLE(); } }
      
      







stm32f7の場合
 static void SystemClock_Config(void) { RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_OscInitTypeDef RCC_OscInitStruct; /* Enable HSE Oscillator and activate PLL with HSE as source */ 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"); } /* activate the OverDrive to reach the 216 Mhz Frequency */ if(HAL_PWREx_EnableOverDrive() != HAL_OK) { printf(">>> SystemClock_Config failed\n"); } /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */ 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; /* Enable HSE Oscillator and activate PLL with HSE as source */ 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) { // Error_Handler(); } /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */ 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_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2)!= HAL_OK) { // Error_Handler(); } }
      
      







実際には、その後システムで割り込みとタイマーが機能し、これはプリエンプティブマルチタスクを整理するのに十分です。 これらのマイクロコントローラーでマルチタスクのパワーを感じるためには、httpサーバーとtelnetサーバーを実行するだけで十分ですが、そのためにはネットワークカードドライバーを実装する必要があります。 ここでは、共通のドライバーがあり、レッグなどを設定する特定のプラットフォーム用に個別の構成があるという意味で、UARTと似ています。



イーサネット



構成から始めましょう。 stm32f3にはイーサネットコントローラーがないため、stm32f4およびstm32f7のみのバージョンがあります。



stm32f4の場合
 void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) { /*(##) Enable the Ethernet interface clock using (+++) __HAL_RCC_ETHMAC_CLK_ENABLE(); (+++) __HAL_RCC_ETHMACTX_CLK_ENABLE(); (+++) __HAL_RCC_ETHMACRX_CLK_ENABLE(); (##) Initialize the related GPIO clocks (##) Configure Ethernet pin-out (##) Configure Ethernet NVIC interrupt (IT mode) */ GPIO_InitTypeDef GPIO_InitStructure; /* Enable ETHERNET clock __HAL_RCC_ETHMAC_CLK_ENABLE(); __HAL_RCC_ETHMACTX_CLK_ENABLE(); __HAL_RCC_ETHMACRX_CLK_ENABLE(); */ __HAL_RCC_ETH_CLK_ENABLE(); /* Enable GPIOs clocks */ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); /* Ethernet pins configuration ************************************************/ /* ETH_MDIO --------------> PA2 ETH_MDC ---------------> PC1 ETH_RMII_REF_CLK-------> PA1 ETH_RMII_CRS_DV -------> PA7 ETH_MII_RX_ER -------> PB10 ETH_RMII_RXD0 -------> PC4 ETH_RMII_RXD1 -------> PC5 ETH_RMII_TX_EN -------> PB11 ETH_RMII_TXD0 -------> PB12 ETH_RMII_TXD1 -------> PB13 ETH_RST_PIN -------> PE2 */ /* Configure PA1,PA2 and PA7 */ 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); /* Configure PB10,PB11,PB12 and PB13 */ GPIO_InitStructure.Pin = GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13; /* GPIO_InitStructure.Alternate = GPIO_AF11_ETH; */ HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); /* Configure PC1, PC4 and PC5 */ GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; /* GPIO_InitStructure.Alternate = GPIO_AF11_ETH; */ HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); if (heth->Init.MediaInterface == ETH_MEDIA_INTERFACE_MII) { /* Output HSE clock (25MHz) on MCO pin (PA8) to clock the PHY */ HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1); } /* Configure the PHY RST pin */ 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; /* Enable GPIOs clocks */ __HAL_RCC_GPIOA_CLK_ENABLE() ; __HAL_RCC_GPIOC_CLK_ENABLE() ; __HAL_RCC_GPIOG_CLK_ENABLE(); /* Ethernet pins configuration *****************************************/ /* RMII_REF_CLK ----------------------> PA1 RMII_MDIO -------------------------> PA2 RMII_MDC --------------------------> PC1 RMII_MII_CRS_DV -------------------> PA7 RMII_MII_RXD0 ---------------------> PC4 RMII_MII_RXD1 ---------------------> PC5 RMII_MII_RXER ---------------------> PG2 RMII_MII_TX_EN --------------------> PG11 RMII_MII_TXD0 ---------------------> PG13 RMII_MII_TXD1 ---------------------> PG14 */ /* Configure PA1, PA2 and PA7 */ 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); /* Configure PC1, PC4 and PC5 */ GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5; HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); /* Configure PG2, PG11, PG13 and PG14 */ GPIO_InitStructure.Pin = GPIO_PIN_2 | GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14; HAL_GPIO_Init(GPIOG, &GPIO_InitStructure); /* Enable the Ethernet global Interrupt */ HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); HAL_NVIC_EnableIRQ(ETH_IRQn); /* Enable ETHERNET clock */ __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]) { //uint32_t regvalue; int err; memset(&stm32_eth_handler, 0, sizeof(stm32_eth_handler)); stm32_eth_handler.Instance = (ETH_TypeDef *) ETH_BASE; /* Fill ETH_InitStructure parametrs */ stm32_eth_handler.Init.MACAddr = mac; stm32_eth_handler.Init.AutoNegotiation = ETH_AUTONEGOTIATION_DISABLE; //stm32_eth_handler.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE; stm32_eth_handler.Init.Speed = ETH_SPEED_100M; stm32_eth_handler.Init.DuplexMode = ETH_MODE_FULLDUPLEX; stm32_eth_handler.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII; stm32_eth_handler.Init.ChecksumMode = ETH_CHECKSUM_BY_SOFTWARE;//ETH_CHECKSUM_BY_HARDWARE; stm32_eth_handler.Init.PhyAddress = PHY_ADDRESS; stm32_eth_handler.Init.RxMode = ETH_RXINTERRUPT_MODE; if (HAL_OK != (err = HAL_ETH_Init(&stm32_eth_handler))) { log_error("HAL_ETH_Init err %d\n", err); } if (stm32_eth_handler.State == HAL_ETH_STATE_READY) { log_error("STATE_READY sp %d duplex %d\n", stm32_eth_handler.Init.Speed, stm32_eth_handler.Init.DuplexMode); } /*(#)Initialize Ethernet DMA Descriptors in chain mode and point to allocated buffers:*/ HAL_ETH_DMATxDescListInit(&stm32_eth_handler, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); /*for Transmission process*/ if (HAL_OK != (err = HAL_ETH_DMARxDescListInit(&stm32_eth_handler, DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB))) { /*for Reception process*/ log_error("HAL_ETH_DMARxDescListInit %d\n", err); } /* (#)Enable MAC and DMA transmission and reception: */ HAL_ETH_Start(&stm32_eth_handler); }
      
      







送信
 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); /* Prepare transmit descriptors to give to DMA */ 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; } /* Frame received */ if (__HAL_ETH_DMA_GET_FLAG(heth, ETH_DMA_FLAG_R)) { /* Receive complete callback */ while (NULL != (skb = low_level_input())) { skb->dev = nic_p; show_packet(skb->mac.raw, skb->len, "rx"); netif_rx(skb); } /* Clear the Eth DMA Rx IT pending bits */ __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; /* get received frame */ if (HAL_ETH_GetReceivedFrame_IT(&stm32_eth_handler) != HAL_OK) return NULL; /* Obtain the size of the packet and put it into the "len" variable. */ len = stm32_eth_handler.RxFrameInfos.length; buffer = (uint8_t *) stm32_eth_handler.RxFrameInfos.buffer; /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */ skb = skb_alloc(len); /* copy received frame to pbuf chain */ if (skb != NULL) { memcpy(skb->mac.raw, buffer, len); } /* Release descriptors to DMA */ dmarxdesc = stm32_eth_handler.RxFrameInfos.FSRxDesc; /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ for (i=0; i< stm32_eth_handler.RxFrameInfos.SegCount; i++) { dmarxdesc->Status |= ETH_DMARXDESC_OWN; dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr); } /* Clear Segment_Count */ stm32_eth_handler.RxFrameInfos.SegCount =0; /* When Rx Buffer unavailable flag is set: clear it and resume reception */ if ((stm32_eth_handler.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET) { /* Clear RBUS ETHERNET DMA flag */ stm32_eth_handler.Instance->DMASR = ETH_DMASR_RBUS; /* Resume DMA reception */ stm32_eth_handler.Instance->DMARPDR = 0; } return skb; }
      
      







実際、これがドライバー全体です。実装後、Emboxにあるすべてのネットワークユーティリティが動作します。アプリケーションについては、以前の記事(pjsipおよびhttpdで読むことができ



、リストされているstm32cubeベースのドライバーに加えて、spi、i2c、lcd、アクセラレーター、ジャイロスコープなども実装されています。



PS私たちのレポートはOSDAY(モスクワで5月23日〜24日)で受け入れられました。プロジェクトについてライブで聞くことに興味があるなら、大歓迎です。



All Articles