FreeModbusのSTM32への移植。 ディナールのバージョン

こんにちは、マイクロコントローラのプログラミングの愛好家と専門家。 この記事では、freemodbusライブラリをSTM32F100(discovery vlにあるもの)に移植することについて説明します。 はい、habrahabrにはすでに同様の記事がありますが、私には最も成功しているわけではないようです。 Modbus RTUをスレーブモードで使用します。 freemodbusライブラリをオペレーティングシステムなしでプラットフォームに正常に移植するには、次の3つの手順を完了する必要があります。



1.ファイルport.hを登録します

2.タイマーを設定する

3. usartを構成する



だから、計画が策定されました-それは仕事の時間です。



便宜上、STM Cube for IARを使用してプロジェクトを生成します。 デバッグを有効にし、タイマーを設定する必要があります。また、ボード上にあるクォーツも使用しました。

STMキューブのスクリーンショット
画像

画像

画像

画像





プロジェクトを生成します。 freemodbus-v1.5.0のソースをダウンロードします。 modbusフォルダーが必要です。 \ Driversのプロジェクトフォルダーに配置します。

画像



そこで、freemodbus-v1.5.0 \ demo \ BAREからポートフォルダーを配置します。

画像



プロジェクトを開き、コピーしたばかりのソースコードを添付しましょう。

画像



次に、[プリプロセッサ]タブのプロジェクトオプションでフォルダへのパスを指定する必要があります。

画像



何らかの理由で、STM QUBEはデバイスとして何も示していません。 修正中です。

画像

画像



この段階では、警告はありますが、プロジェクトは進行中です。 直接移植に進みます。 port.hを開きます。 操作の原子性を保証する関数を宣言します。 また、UARTの関数宣言もここに配置します。

void __critical_enter(void); void __critical_exit(void); #define ENTER_CRITICAL_SECTION( ) ( __critical_enter( ) ) #define EXIT_CRITICAL_SECTION( ) ( __critical_exit( ) ) void prvvUARTTxReadyISR( void ); void prvvUARTRxISR( void );
      
      







main()で定義を書きました。

 static uint32_t lock_nesting_count = 0; void __critical_enter(void) { __disable_irq(); ++lock_nesting_count; } void __critical_exit(void) { /* Unlock interrupts only when we are exiting the outermost nested call. */ --lock_nesting_count; if (lock_nesting_count == 0) { __enable_irq(); } }
      
      





タイマーのライオン部分は、Qubeによってセットアップされました。 少しporttimer.cを追加するだけです。 この部分は完全にHALで記述されており、追加のコメントは不要です。

porttimer.c
 /* ----------------------- Platform includes --------------------------------*/ #include "port.h" #include "stm32f1xx_hal.h" /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" /* ----------------------- static functions ---------------------------------*/ static void prvvTIMERExpiredISR( void ); extern TIM_HandleTypeDef htim6; uint16_t timeout = 0; volatile uint16_t counter = 0; /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) { timeout = usTim1Timerout50us; return TRUE; } void vMBPortTimersEnable( ) { /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */ counter=0; HAL_TIM_Base_Start_IT(&htim6); } void vMBPortTimersDisable( ) { /* Disable any pending timers. */ HAL_TIM_Base_Stop_IT(&htim6); } /* Create an ISR which is called whenever the timer has expired. This function * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that * the timer has expired. */ static void prvvTIMERExpiredISR( void ) { ( void )pxMBPortCBTimerExpired( ); } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if((++counter)>=timeout) { prvvTIMERExpiredISR(); } }
      
      







すべてが計画どおりに進んでいることを確認します。 タイミングが予想と一致することを確認します。 祖父の方法であるオシロスコープを確認します。 1msのインパルスを取得する必要があります。 vMBPortTimersDisableは機能しますか-チェックしません=)



一時的に書き込みます:

画像



そしてメイン()で:

画像



私たちは見ます:

画像



ここで最も興味深いのは、UART =)の設定です。xMBPortSerialInitとvMBPortSerialEnableを記述することから開始する必要があります。 ライブラリのGetByteはcharを受け入れるため、原則として9ビットメッセージを扱うという原則を排除します。 vMBPortSerialEnableを作成するために、USART割り込みスキームを見てみましょう。

画像



受信時に割り込みを有効にするには、RXNEIE:RXNE割り込み有効を有効にする必要があり、イベントでトランスミッターの準備ができていることが必要です-TXEIE:TXE割り込み有効。



受信したバイトはレジスタhuart_m-> Instance-> DRにあります。 このレジスタに書き込むと転送が発生します。 すべてがシンプルです。 USARTでの作業の利便性のために、stm32f1xx_hal_uart.cをプロジェクトに追加し、HAL_UART_MODULE_ENABLEDを保護します。 あまり言葉を書きませんが、中身を見せます。

portserial.c
 #include "port.h" #include "stm32f1xx_hal.h" /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" /* ----------------------- static functions ---------------------------------*/ UART_HandleTypeDef huart_m; HAL_StatusTypeDef USART_Init(UART_HandleTypeDef *huart); void USART_MspInit(UART_HandleTypeDef* huart); static void USART_SetConfig(UART_HandleTypeDef *huart); /* ----------------------- Start implementation -----------------------------*/ void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) { /* If xRXEnable enable serial receive interrupts. If xTxENable enable * transmitter empty interrupts. */ if(xRxEnable) { __HAL_UART_ENABLE_IT(&huart_m, UART_IT_RXNE); } else { __HAL_UART_DISABLE_IT(&huart_m, UART_IT_RXNE); } if(xTxEnable) { __HAL_UART_ENABLE_IT(&huart_m, UART_IT_TXE); } else { __HAL_UART_DISABLE_IT(&huart_m, UART_IT_TXE); } } BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ) { switch (ucPORT) { case 0: huart_m.Instance = USART1; break; case 1: huart_m.Instance = USART2; break; case 2: huart_m.Instance = USART3; break; default: return FALSE; } huart_m.Init.BaudRate = ulBaudRate; switch (ucDataBits) { case 8: huart_m.Init.WordLength = UART_WORDLENGTH_8B; break; default: return FALSE; } switch (eParity) { case MB_PAR_NONE: huart_m.Init.Parity = UART_PARITY_NONE; break; case MB_PAR_EVEN: huart_m.Init.Parity = UART_PARITY_EVEN; break; case MB_PAR_ODD: huart_m.Init.Parity = UART_PARITY_ODD; break; default: return FALSE; } huart_m.Init.StopBits = UART_STOPBITS_1; huart_m.Init.Mode = UART_MODE_TX_RX; huart_m.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart_m.Init.OverSampling = UART_OVERSAMPLING_16; return (HAL_OK == USART_Init(&huart_m)); } BOOL xMBPortSerialPutByte( CHAR ucByte ) { /* Put a byte in the UARTs transmit buffer. This function is called * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been * called. */ huart_m.Instance->DR=ucByte; return TRUE; } BOOL xMBPortSerialGetByte( CHAR * pucByte ) { /* Return the byte in the UARTs receive buffer. This function is called * by the protocol stack after pxMBFrameCBByteReceived( ) has been called. */ if(huart_m.Init.Parity == UART_PARITY_NONE) { *pucByte = (uint8_t)(huart_m.Instance->DR & (uint8_t)0x00FF); } else { *pucByte = (uint8_t)(huart_m.Instance->DR & (uint8_t)0x007F); } return TRUE; } /* Create an interrupt handler for the transmit buffer empty interrupt * (or an equivalent) for your target processor. This function should then * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that * a new character can be sent. The protocol stack will then call * xMBPortSerialPutByte( ) to send the character. */ void prvvUARTTxReadyISR( void ) { pxMBFrameCBTransmitterEmpty( ); } /* Create an interrupt handler for the receive interrupt for your target * processor. This function should then call pxMBFrameCBByteReceived( ). The * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the * character. */ void prvvUARTRxISR( void ) { pxMBFrameCBByteReceived( ); } HAL_StatusTypeDef USART_Init(UART_HandleTypeDef *huart) { /* Check the UART handle allocation */ if(huart == NULL) { return HAL_ERROR; } /* Check the parameters */ if(huart->Init.HwFlowCtl != UART_HWCONTROL_NONE) { /* The hardware flow control is available only for USART1, USART2, USART3 */ assert_param(IS_UART_HWFLOW_INSTANCE(huart->Instance)); assert_param(IS_UART_HARDWARE_FLOW_CONTROL(huart->Init.HwFlowCtl)); } else { assert_param(IS_UART_INSTANCE(huart->Instance)); } assert_param(IS_UART_WORD_LENGTH(huart->Init.WordLength)); assert_param(IS_UART_OVERSAMPLING(huart->Init.OverSampling)); if(huart->State == HAL_UART_STATE_RESET) { /* Allocate lock resource and initialize it */ huart->Lock = HAL_UNLOCKED; /* Init the low level hardware */ USART_MspInit(huart); } huart->State = HAL_UART_STATE_BUSY; /* Disable the peripheral */ __HAL_UART_DISABLE(huart); /* Set the UART Communication parameters */ USART_SetConfig(huart); /* In asynchronous mode, the following bits must be kept cleared: - LINEN and CLKEN bits in the USART_CR2 register, - SCEN, HDSEL and IREN bits in the USART_CR3 register.*/ CLEAR_BIT(huart->Instance->CR2, (USART_CR2_LINEN | USART_CR2_CLKEN)); CLEAR_BIT(huart->Instance->CR3, (USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN)); /* Enable the peripheral */ __HAL_UART_ENABLE(huart); /* Initialize the UART state */ huart->ErrorCode = HAL_UART_ERROR_NONE; huart->State= HAL_UART_STATE_READY; return HAL_OK; } void USART_MspInit(UART_HandleTypeDef* huart) { GPIO_InitTypeDef GPIO_InitStruct; if(huart->Instance==USART1) { /* USER CODE BEGIN USART1_MspInit 0 */ /* USER CODE END USART1_MspInit 0 */ /* Peripheral clock enable */ __HAL_RCC_USART1_CLK_ENABLE(); /**USART1 GPIO Configuration PA9 ------> USART1_TX PA10 ------> USART1_RX */ GPIO_InitStruct.Pin = GPIO_PIN_9; 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_10; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* Peripheral interrupt init */ HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART1_IRQn); /* USER CODE BEGIN USART1_MspInit 1 */ /* USER CODE END USART1_MspInit 1 */ } else if(huart->Instance==USART2) { /* USER CODE BEGIN USART2_MspInit 0 */ /* USER CODE END USART2_MspInit 0 */ /* Peripheral clock enable */ __HAL_RCC_USART2_CLK_ENABLE(); /**USART2 GPIO Configuration PA2 ------> USART2_TX PA3 ------> USART2_RX */ GPIO_InitStruct.Pin = GPIO_PIN_2; 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_3; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* Peripheral interrupt init */ HAL_NVIC_SetPriority(USART2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART2_IRQn); /* USER CODE BEGIN USART2_MspInit 1 */ /* USER CODE END USART2_MspInit 1 */ } else if(huart->Instance==USART3) { /* USER CODE BEGIN USART3_MspInit 0 */ /* USER CODE END USART3_MspInit 0 */ /* Peripheral clock enable */ __HAL_RCC_USART3_CLK_ENABLE(); /**USART3 GPIO Configuration PB10 ------> USART3_TX PB11 ------> USART3_RX */ GPIO_InitStruct.Pin = GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* Peripheral interrupt init */ HAL_NVIC_SetPriority(USART3_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART3_IRQn); /* USER CODE BEGIN USART3_MspInit 1 */ /* USER CODE END USART3_MspInit 1 */ } } static void USART_SetConfig(UART_HandleTypeDef *huart) { uint32_t tmpreg = 0x00; /* Check the parameters */ assert_param(IS_UART_BAUDRATE(huart->Init.BaudRate)); assert_param(IS_UART_STOPBITS(huart->Init.StopBits)); assert_param(IS_UART_PARITY(huart->Init.Parity)); assert_param(IS_UART_MODE(huart->Init.Mode)); /*------- UART-associated USART registers setting : CR2 Configuration ------*/ /* Configure the UART Stop Bits: Set STOP[13:12] bits according * to huart->Init.StopBits value */ MODIFY_REG(huart->Instance->CR2, USART_CR2_STOP, huart->Init.StopBits); /*------- UART-associated USART registers setting : CR1 Configuration ------*/ /* Configure the UART Word Length, Parity and mode: Set the M bits according to huart->Init.WordLength value Set PCE and PS bits according to huart->Init.Parity value Set TE and RE bits according to huart->Init.Mode value */ tmpreg = (uint32_t)huart->Init.WordLength | huart->Init.Parity | huart->Init.Mode ; MODIFY_REG(huart->Instance->CR1, (uint32_t)(USART_CR1_M | USART_CR1_PCE | USART_CR1_PS | USART_CR1_TE | USART_CR1_RE), tmpreg); /*------- UART-associated USART registers setting : CR3 Configuration ------*/ /* Configure the UART HFC: Set CTSE and RTSE bits according to huart->Init.HwFlowCtl value */ MODIFY_REG(huart->Instance->CR3, (USART_CR3_RTSE | USART_CR3_CTSE), huart->Init.HwFlowCtl); /*------- UART-associated USART registers setting : BRR Configuration ------*/ if((huart->Instance == USART1)) { huart->Instance->BRR = UART_BRR_SAMPLING16(HAL_RCC_GetPCLK2Freq(), huart->Init.BaudRate); } else { huart->Instance->BRR = UART_BRR_SAMPLING16(HAL_RCC_GetPCLK1Freq(), huart->Init.BaudRate); } }
      
      





次に、stm32f1xx_it.cで割り込みを構成する必要があります。



 void DINAR_UART_IRQHandler(UART_HandleTypeDef *huart) { uint32_t tmp_flag = 0, tmp_it_source = 0; tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE); tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE); /* UART in mode Receiver ---------------------------------------------------*/ if((tmp_flag != RESET) && (tmp_it_source != RESET)) { prvvUARTRxISR( ); } tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_TXE); tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_TXE); /* UART in mode Transmitter ------------------------------------------------*/ if((tmp_flag != RESET) && (tmp_it_source != RESET)) { prvvUARTTxReadyISR( ); } } void USART1_IRQHandler(void) { DINAR_UART_IRQHandler(&huart_m); } void USART2_IRQHandler(void) { DINAR_UART_IRQHandler(&huart_m); } void USART3_IRQHandler(void) { DINAR_UART_IRQHandler(&huart_m); }
      
      







タイマーの場合と同様に、すべてが計画どおりに進んでいることを確認する必要があります。 確認します。 一時的に書き込みます:

画像



ブレークポイントを設定し、バイトを受信したときに割り込みが発生することを確認します。 私はヘラクレスを通して送りました。

画像

画像



送信機もチェックします。 一時的に書き込みます:

画像



私たちは見ます:

画像



次に、Modbus Pollを使用してデバイスをポーリングしてみましょう。

デモの例をご覧ください
画像

画像

画像



調査を実行する
画像

画像

画像



動作します! この記事が、私などの初心者がこのシンプルだが同時に有用なプロトコルを実装するのに役立つことを願っています。



All Articles