STM32CubeMXに関する事 2 リングバッファではないだと、、、 [STM32F]
STM32CubeMXコードジェネレータが出力するUART(USART)のコードについて。
以下の様な送信/受信関数が提供されます。
HAL_UART_Transmit、HAL_UART_Receiveはブロッキングインタフェースで、内部で送信レジスタ、受信レジスタを監視して動作していますが、それ以下の関数は割り込みとDMAを併用するノンブロッキングインタフェースとなっています。
それぞれの関数は、引数のデータ数に送信や受信が達する事で完了となります。
それ以前に再びこの関数が呼ばれた時は、戻り値にHAL_BUSYが返ります。
つまり問題は受信時に処理を完了させる要素が引数の受信データ数となっている点ですね。
送信はイイのですよ、事前に送信データのサイズは明確にできますから。
ですが受信の場合相手がいる訳ですから、実際に受信するデータの数が事前に判る状況はあまり無いでしょう。
100byteデータを受信するとなっても、通信ですから途中でなにが起こるか判らない。
受信サイズを100byteとしてしまって途中で1byteこけた場合はそのまま処理が永久に?完了しない事となります。
ブロッキングインタフェースの場合は引数にタイムアウトを設定できるので、タイムアウトを使う事でプログラムが不動となる事は避けられそうです。
しかしノンブロッキングインタフェースではタイムアウトは設定されていないので、1byteでも受信数が少ない場合は不動となる可能性が有ります。
逆に言えば受信データ数には1を超える数字を設定しなければイイ!と言う、高機能な関数を消極的な使い方をすれば解決できそうです、、、orz
ついでにリングバッファを構成しましょう。
※usart1とusart2を使えるようにした。
※USARTのコンフィギュレーションは別のところで行ってる。
※includeなどは省いている。
※usart2はループバック、usart1はTeratermに接続している。
以下の様な送信/受信関数が提供されます。
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_UART_Transmit、HAL_UART_Receiveはブロッキングインタフェースで、内部で送信レジスタ、受信レジスタを監視して動作していますが、それ以下の関数は割り込みとDMAを併用するノンブロッキングインタフェースとなっています。
それぞれの関数は、引数のデータ数に送信や受信が達する事で完了となります。
それ以前に再びこの関数が呼ばれた時は、戻り値にHAL_BUSYが返ります。
つまり問題は受信時に処理を完了させる要素が引数の受信データ数となっている点ですね。
送信はイイのですよ、事前に送信データのサイズは明確にできますから。
ですが受信の場合相手がいる訳ですから、実際に受信するデータの数が事前に判る状況はあまり無いでしょう。
100byteデータを受信するとなっても、通信ですから途中でなにが起こるか判らない。
受信サイズを100byteとしてしまって途中で1byteこけた場合はそのまま処理が永久に?完了しない事となります。
ブロッキングインタフェースの場合は引数にタイムアウトを設定できるので、タイムアウトを使う事でプログラムが不動となる事は避けられそうです。
しかしノンブロッキングインタフェースではタイムアウトは設定されていないので、1byteでも受信数が少ない場合は不動となる可能性が有ります。
逆に言えば受信データ数には1を超える数字を設定しなければイイ!と言う、高機能な関数を消極的な使い方をすれば解決できそうです、、、orz
ついでにリングバッファを構成しましょう。
※usart1とusart2を使えるようにした。
※USARTのコンフィギュレーションは別のところで行ってる。
※includeなどは省いている。
※usart2はループバック、usart1はTeratermに接続している。
HAL_StatusTypeDef usartWrite( UART_HandleTypeDef *huart, const uint8_t *data, uint16_t size ); HAL_StatusTypeDef usartPutc( UART_HandleTypeDef *huart, char c ); HAL_StatusTypeDef usartPuts( UART_HandleTypeDef *huart, const char *str ); int usartGetc( UART_HandleTypeDef *huart ); int usartAvailable( UART_HandleTypeDef *huart ); static struct P_USART_RCV_INFORMATIONS { char *rcvBuf; uint16_t wptr,rptr; uint16_t overWrite; uint16_t bufferSize; } pUsartRcvInfo[2]; static char rx1Buffer[ 256 ]; /* recieve ring buffer. */ static char rx2Buffer[ 256 ]; /* recieve ring buffer. */ void user( void ) { volatile int count = 1; HAL_StatusTypeDef result; pUsartRcvInfo[0].rcvBuf = rx1Buffer; pUsartRcvInfo[0].wptr = pUsartRcvInfo[0].rptr = pUsartRcvInfo[0].overWrite = 0; pUsartRcvInfo[0].bufferSize = sizeof(rx1Buffer); pUsartRcvInfo[1].rcvBuf = rx2Buffer; pUsartRcvInfo[1].wptr = pUsartRcvInfo[1].rptr = pUsartRcvInfo[1].overWrite = 0; pUsartRcvInfo[1].bufferSize = sizeof(rx2Buffer); HAL_UART_Receive_IT( &huart1, (uint8_t *)&pUsartRcvInfo[0].rcvBuf[ pUsartRcvInfo[0].wptr ], 1 ); HAL_UART_Receive_IT( &huart2, (uint8_t *)&pUsartRcvInfo[1].rcvBuf[ pUsartRcvInfo[1].wptr ], 1 ); while (1) { char buf[128]; sprintf( buf, "hello world. count = %d\r\n", count++ ); result = usartPuts( &huart2, (const char *)buf ); if( result != HAL_OK ) { debugPrint( "uart2 transmit was not success. error code = %d\r\n", result ); } HAL_Delay( 10UL ); while( 1 ) { int c = usartGetc( &huart2 ); if( c < 0 ) break; usartPutc( &huart1, c ); } HAL_Delay( 90UL ); } } HAL_StatusTypeDef usartWrite( UART_HandleTypeDef *huart, const uint8_t *data, uint16_t size ) { HAL_StatusTypeDef result; //while( (result = HAL_UART_Transmit( huart, (uint8_t *)data, size, 100UL )) == HAL_BUSY ) {} while( (result = HAL_UART_Transmit_IT( huart, (uint8_t *)data, size )) == HAL_BUSY ) {} return result; } HAL_StatusTypeDef usartPutc( UART_HandleTypeDef *huart, char c ) { return usartWrite( huart, (const uint8_t *)&c, 1 ); } HAL_StatusTypeDef usartPuts( UART_HandleTypeDef *huart, const char *str ) { return usartWrite( huart, (const uint8_t *)str, strlen(str) ); } int usartGetc( UART_HandleTypeDef *huart ) { int c; uint16_t next; if( huart->Instance == USART1 ) { if( pUsartRcvInfo[0].rptr == pUsartRcvInfo[0].wptr ) return (-1); /* */ next = pUsartRcvInfo[0].rptr + 1; if( next >= pUsartRcvInfo[0].bufferSize ) next = 0; c = pUsartRcvInfo[0].rcvBuf[ pUsartRcvInfo[0].rptr ] & 0x00ff; pUsartRcvInfo[0].rptr = next; } else if( huart->Instance == USART2 ) { if( pUsartRcvInfo[1].rptr == pUsartRcvInfo[1].wptr ) return (-1); /* */ next = pUsartRcvInfo[1].rptr + 1; if( next >= pUsartRcvInfo[1].bufferSize ) next = 0; c = pUsartRcvInfo[1].rcvBuf[ pUsartRcvInfo[1].rptr ] & 0x00ff; pUsartRcvInfo[1].rptr = next; } return c; } int usartAvailable( UART_HandleTypeDef *huart ) { if( huart->Instance == USART1 ) { if( pUsartRcvInfo[0].wptr >= pUsartRcvInfo[0].rptr ) { return pUsartRcvInfo[0].wptr - pUsartRcvInfo[0].rptr; } else { return (pUsartRcvInfo[0].bufferSize + pUsartRcvInfo[0].wptr) - pUsartRcvInfo[0].rptr; } } else if( huart->Instance == USART2 ) { if( pUsartRcvInfo[1].wptr >= pUsartRcvInfo[1].rptr ) { return pUsartRcvInfo[1].wptr - pUsartRcvInfo[1].rptr; } else { return (pUsartRcvInfo[1].bufferSize + pUsartRcvInfo[1].wptr) - pUsartRcvInfo[1].rptr; } } return 0; } #if 0 /** * @brief Tx Transfer completed callbacks. * @param huart: pointer to a UART_HandleTypeDef structure that contains * the configuration information for the specified UART module. * @retval None */ void HAL_UART_TxCpltCallback( UART_HandleTypeDef *huart ) { } #endif /** * @brief Rx Transfer completed callbacks. * @param huart: pointer to a UART_HandleTypeDef structure that contains * the configuration information for the specified UART module. * @retval None */ void HAL_UART_RxCpltCallback( UART_HandleTypeDef *huart ) { uint16_t next; if( huart->Instance == USART1 ) { next = pUsartRcvInfo[0].wptr + 1; if( next >= pUsartRcvInfo[0].bufferSize ) next = 0; if( next == pUsartRcvInfo[0].rptr ) /* do not permit over write. */ { pUsartRcvInfo[0].overWrite++; return; } pUsartRcvInfo[0].wptr = next; HAL_UART_Receive_IT( huart, (uint8_t *)&pUsartRcvInfo[0].rcvBuf[ pUsartRcvInfo[0].wptr ], 1 ); } else if( huart->Instance == USART2 ) { next = pUsartRcvInfo[1].wptr + 1; if( next >= pUsartRcvInfo[1].bufferSize ) next = 0; if( next == pUsartRcvInfo[1].rptr ) /* do not permit over write. */ { pUsartRcvInfo[1].overWrite++; return; } pUsartRcvInfo[1].wptr = next; HAL_UART_Receive_IT( huart, (uint8_t *)&pUsartRcvInfo[1].rcvBuf[ pUsartRcvInfo[1].wptr ], 1 ); } }
STM32CubeMXに関する事 1 デバッグできん! [STM32F]
さて、STマイクロのSTM32Fシリーズでは、従来はSPLライブラリが提供されてたのですが、最近リリースされるマイコンにはSPLライブラリは提供されず、代わりにSTM32CubeMXと言うコードジェネレータを使え!となっているので、しょうがないので触り始めて嵌っています、、、orz
今回の問題は、STM32CubeMXが出力するREMAPに関するマクロです。
ターゲットはSTM32F103VETです。
嵌ったのはI2Cを有効にするとデバッガーが暴走する!ってものでした。
くだんのマクロ集の抜粋です。( stm32f1xx_hal_gpio_ex.h )
SET BITは以下 ( stm32f1xx.h )
つまりRead Modify Writeをします。
ところがSTM32F103のREMAPを制御しているMAPRレジスタの説明にはこの様に書かれています。
なので、読み出しても正確な値ではない!
じっさいのところこのマクロを処理するとデバッグできなくなるので、逆アセンブルしてレジスタを追ってみると
100: JTAG-DP Disabled and SW-DP Disabled
が入ってきてしまいます。これでは動きませんよね、、、
対策はREMAPを行っているところでは必ず後ろに
__HAL_AFIO_REMAP_SWJ_NOJTAG();
などのデバッガを有効にするマクロを追加する?
( stm32f1xx_hal_msp.c )
しかしCubeMXでコードを生成する度にこの対策が消されてしまうのでは?と思っています、、、ハァ。
今回の問題は、STM32CubeMXが出力するREMAPに関するマクロです。
ターゲットはSTM32F103VETです。
嵌ったのはI2Cを有効にするとデバッガーが暴走する!ってものでした。
くだんのマクロ集の抜粋です。( stm32f1xx_hal_gpio_ex.h )
/** * @brief Enable the remapping of I2C1 alternate function SCL and SDA. * @note ENABLE: Remap (SCL/PB8, SDA/PB9) * @retval None */ #define __HAL_AFIO_REMAP_I2C1_ENABLE() SET_BIT(AFIO->MAPR, AFIO_MAPR_I2C1_REMAP)
SET BITは以下 ( stm32f1xx.h )
/** @addtogroup Exported_macros * @{ */ #define SET_BIT(REG, BIT) ((REG) |= (BIT)) #define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT)) #define READ_BIT(REG, BIT) ((REG) & (BIT)) #define CLEAR_REG(REG) ((REG) = (0x0)) #define WRITE_REG(REG, VAL) ((REG) = (VAL)) #define READ_REG(REG) ((REG)) #define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) #define POSITION_VAL(VAL) (__CLZ(__RBIT(VAL)))
つまりRead Modify Writeをします。
ところがSTM32F103のREMAPを制御しているMAPRレジスタの説明にはこの様に書かれています。
なので、読み出しても正確な値ではない!
じっさいのところこのマクロを処理するとデバッグできなくなるので、逆アセンブルしてレジスタを追ってみると
100: JTAG-DP Disabled and SW-DP Disabled
が入ってきてしまいます。これでは動きませんよね、、、
対策はREMAPを行っているところでは必ず後ろに
__HAL_AFIO_REMAP_SWJ_NOJTAG();
などのデバッガを有効にするマクロを追加する?
( stm32f1xx_hal_msp.c )
void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c) { GPIO_InitTypeDef GPIO_InitStruct; if(hi2c->Instance==I2C1) { /* USER CODE BEGIN I2C1_MspInit 0 */ /* USER CODE END I2C1_MspInit 0 */ /**I2C1 GPIO Configuration PB8 ------> I2C1_SCL PB9 ------> I2C1_SDA */ GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); //__HAL_AFIO_REMAP_I2C1_ENABLE(); // <------ 訂正:コメントアウト // __HAL_AFIO_REMAP_SWJ_NOJTAG(); // <------ 訂正:コメントアウト /* Peripheral clock enable */ __HAL_RCC_I2C1_CLK_ENABLE(); /* USER CODE BEGIN I2C1_MspInit 1 */ uint32_t mapr = AFIO->MAPR; // <------ 訂正:マクロをやめて直接レジスタを操作 mapr &= ~(7UL << 24); mapr |= (2UL << 24); mapr |= (1UL << 1); AFIO->MAPR = mapr; /* USER CODE END I2C1_MspInit 1 */ } }
しかしCubeMXでコードを生成する度にこの対策が消されてしまうのでは?と思っています、、、ハァ。