こんにちは、マイクロコントローラのプログラミングの愛好家と専門家。 この記事では、freemodbusライブラリをSTM32F100(discovery vlにあるもの)に移植することについて説明します。 はい、habrahabrにはすでに同様の
記事がありますが、私には最も成功しているわけではないようです。 Modbus RTUをスレーブモードで使用します。 freemodbusライブラリをオペレーティングシステムなしでプラットフォームに正常に移植するには、次の3つの手順を完了する必要があります。
1.ファイルport.hを登録します
2.タイマーを設定する
3. usartを構成する
だから、計画が策定されました-それは仕事の時間です。
便宜上、STM Cube for IARを使用してプロジェクトを生成します。 デバッグを有効にし、タイマーを設定する必要があります。また、ボード上にあるクォーツも使用しました。
プロジェクトを生成します。 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) { --lock_nesting_count; if (lock_nesting_count == 0) { __enable_irq(); } }
タイマーのライオン部分は、Qubeによってセットアップされました。 少しporttimer.cを追加するだけです。 この部分は完全にHALで記述されており、追加のコメントは不要です。
porttimer.c #include "port.h" #include "stm32f1xx_hal.h" #include "mb.h" #include "mbport.h" static void prvvTIMERExpiredISR( void ); extern TIM_HandleTypeDef htim6; uint16_t timeout = 0; volatile uint16_t counter = 0; BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) { timeout = usTim1Timerout50us; return TRUE; } void vMBPortTimersEnable( ) { counter=0; HAL_TIM_Base_Start_IT(&htim6); } void vMBPortTimersDisable( ) { HAL_TIM_Base_Stop_IT(&htim6); } 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" #include "mb.h" #include "mbport.h" UART_HandleTypeDef huart_m; HAL_StatusTypeDef USART_Init(UART_HandleTypeDef *huart); void USART_MspInit(UART_HandleTypeDef* huart); static void USART_SetConfig(UART_HandleTypeDef *huart); void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) { 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 ) { huart_m.Instance->DR=ucByte; return TRUE; } BOOL xMBPortSerialGetByte( CHAR * pucByte ) { 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; } void prvvUARTTxReadyISR( void ) { pxMBFrameCBTransmitterEmpty( ); } void prvvUARTRxISR( void ) { pxMBFrameCBByteReceived( ); } HAL_StatusTypeDef USART_Init(UART_HandleTypeDef *huart) { if(huart == NULL) { return HAL_ERROR; } if(huart->Init.HwFlowCtl != UART_HWCONTROL_NONE) { 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) { huart->Lock = HAL_UNLOCKED; USART_MspInit(huart); } huart->State = HAL_UART_STATE_BUSY; __HAL_UART_DISABLE(huart); USART_SetConfig(huart); 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)); __HAL_UART_ENABLE(huart); 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) { __HAL_RCC_USART1_CLK_ENABLE(); 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); HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART1_IRQn); } else if(huart->Instance==USART2) { __HAL_RCC_USART2_CLK_ENABLE(); 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); HAL_NVIC_SetPriority(USART2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART2_IRQn); } else if(huart->Instance==USART3) { __HAL_RCC_USART3_CLK_ENABLE(); 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); HAL_NVIC_SetPriority(USART3_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART3_IRQn); } } static void USART_SetConfig(UART_HandleTypeDef *huart) { uint32_t tmpreg = 0x00; 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)); MODIFY_REG(huart->Instance->CR2, USART_CR2_STOP, huart->Init.StopBits); 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); MODIFY_REG(huart->Instance->CR3, (USART_CR3_RTSE | USART_CR3_CTSE), huart->Init.HwFlowCtl); 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); 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); 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を使用してデバイスをポーリングしてみましょう。
動作します! この記事が、私などの初心者がこのシンプルだが同時に有用なプロトコルを実装するのに役立つことを願っています。