2017年01月06日

UART受信について

今回はSTM32でのUART受信処理について紹介します。


環境はこれまで同様、
・STM32F303K8
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(F3_1.6.0)
です。


STM32F3系でなかなかUartの受信ができず、
DMAを使うしかないかなと諦めかけていましたが、
原因が分かったため、紹介します。
(This report is wrote about
  not called HAL_UART_RxCpltCallback probrem)


CubeMXのUart設定でNVIC Settingの
USART Global InterruptをEnableにするとUART受信で
HAL_UART_RxCpltCallback
という関数が呼び出されます。


uart.png

ただ、HALライブラリのバグで呼び出せないという不具合がありました。
不具合ではなく、最終的に関数の呼び出しが必要と判明しました。
対処方法#1で受信できるようになります。

対処方法#1は下記の通りです。
(The coping process #1 is following)

@HAL_UART_RxCpltCallbackを追加。
  (ADD HAL_UART_RxCpltCallback func.)

char UART1_Data;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
     if(UartHandle->Instance==USART1){
          HAL_UART_Receive_IT(&huart1, (uint8_t*) &UART1_Data, 1);      
    }
 }

Amain関数内の初期化後にUartの設定と割り込み有効化
 (Set Uart variable and enable interrupt after init.)

HAL_UART_Receive_IT(&huart1, (uint8_t*) &UART1_Data, 1);

while(1)
{
~~~



以上の設定を行うことで
1文字受信毎にHAL_UART_RxCpltCallbackが呼び出されました。

uart2.png



【参考 reference】
対処方法#2は下記の通りです。
(The coping process #2 is following)


@HAL_UART_RxCpltCallbackを追加。
  (ADD HAL_UART_RxCpltCallback func.)

char UART1_Data;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
     if(UartHandle->Instance==USART1){
          HAL_UART_Receive_IT(&huart1, (uint8_t*) &UART1_Data, 1);      
    }
 }

Amain関数内の初期化後にUartの設定と割り込み有効化
 (Set Uart variable and enable interrupt after init.)

 //Initially pointer is not set!
  huart1.pRxBuffPtr=(uint8_t *)&UART1_Data;  //IMPORTANT!!
 huart1.RxXferCount=1;  //IMPORTANT!!
  huart1.Mask=255;  //If not set, lost first string.
  __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);

while(1)
{
~~~


Bstm32f3xx_it.c内のUSART1_IRQHandler関数を修正
 (Fix USART1_IRQHandler func in stm32f3xx_it.c)

void USART1_IRQHandler(void)
{
  huart1.RxState=HAL_UART_STATE_BUSY_RX;  //IMPORTANT!!
  HAL_UART_IRQHandler(&huart1);
}
posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする

2016年12月08日

math関数使用時のエラー対処

今回はSTM32でのmath関数使用時のエラー対処について紹介します。

以前にご紹介したDSP処理の前設定2でも同じようなエラーがでましたが、
以前はDSPを使用した関数でした。
今回はDSP関数は使用せず、math関数使用時のエラー対処方法です。



環境はこれまで同様、
・STM32F303K8
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(F3_1.6.0)
です。


main.c関数中でpowなど使う分には問題ありませんでしたが、
読み込んだヘッダやドライバ系にpow関数を使うと
下記のようなエラーが発生してコンパイルできませんでした。


w_pow.c:(.text.pow+0x): undefined reference to `__errno'
collect2.exe: error: ld returned 1 exit status
make: *** [STM32F.elf] Error 1


Eclipseのプロジェクトから
「C/C++ Build」→「Settings」→「ToolSettings」→「MCU GCC Linker」
「Libraries」に下記のように
m
を追加します。


ffff.png


上記のようなエラーに遭遇するパターンがいくつかあるため、
今回はmの追加で回避できました。

posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする

2016年11月27日

DSPによるFFT処理

今回はSTM32でのDSPによるFFTについて紹介します。

環境はこれまで同様、
・STM32F303K8
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(F3_1.6.0)
です。

プロジェクトのDSPライブラリ設定等はこちらを参照してください。



ちょっとしたFFT処理であれば、精度よりも速さ重視なことが多いため、
今回はあえてFloatでなくQ15でのFFTを紹介します。
q_15をf32に置き換えればFloatと同じように使えます。


FFT処理は使用するライブラリの関数によって
配列の渡し方や複素数の扱いが異なるため、
間違えて配列オーバーなどしてしまいがちです。
長さに注意しながらコーディングが必要です。


ARMのFFTコード例は下記の通りです。
一部抜粋です。


#define LENGTH_SAMPLES     256 //Num of Real & Img
#define ifftFlag         0
#define doBitReverse    1
#define fftSize            LENGTH_SAMPLES/2

#define FFTLen arm_cfft_sR_q15_len128


q15_t FFT_Input[LENGTH_SAMPLES];
q15_t FFT_Output[LENGTH_SAMPLES/2];

enum{ WAIT,RUN};
//実部と虚部で256個でAD変換値は実部のため、サイズは128
enum{ ADC_BUFFER_LENGTH = LENGTH_SAMPLES/2 };
//DMA転送でHalfWordにつき16bit長で定義
static uint16_t g_ADCBuffer[ADC_BUFFER_LENGTH];

//AD変換後に呼び出される
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)
{
 
    HAL_TIM_Base_Stop_IT(&htim2);
    HAL_ADC_Stop_DMA(&hadc1);

    for(int i=0;i<LENGTH_SAMPLES/2;i++){
        int tmp;
        tmp= g_ADCBuffer[i];
        //dsPICのFFTは構造体で実部と虚部を格納するが、
        //ARMでは配列に実部と虚部を交互に格納する

        FFT_Input[2*i]=tmp;   //Real part
        FFT_Input[2*i+1]=0.0;   //Img part
    }
    fft_status=RUN;
}

int main(void)
{
 //~~初期化処理~~//

  while (1)
  {

     if(fft_status==RUN){

             arm_cfft_q15(&FFTLen, FFT_Input, ifftFlag, doBitReverse);
             arm_cmplx_mag_q15(FFT_Input, FFT_Output, fftSize);
             arm_max_q15(FFT_Output, fftSize, &maxValue, &testIndex);
        
        //サンプル数は実部と虚部で256個
        //fftsizeは128個
        //FFT_Outputには0~127でFFT結果が格納されるが、
        //64~127は対称の値が入っているだけ
        //実際に使うのはFFT_Outputの中で0~63のデータ
        for(int j=0;j<fftSize/2;j++){
                 printf("%d, ",FFT_Output[j]);
             }
                fft_status=WAIT;
             HAL_ADC_Start_DMA(&hadc1, g_ADCBuffer, ADC_BUFFER_LENGTH);
             HAL_TIM_Base_Start_IT(&htim2);
     }
     else{
             //なぜか何かしらの処理を入れないとwhileに入らないのでnopを入れる
             __asm volatile("nop");
     }
 }
}


N555で35kHz前後で発振させた信号を読み込むと
下記のように35kHz前後の結果となりました。

fftres.png



DMA転送で設定をHalf Wordにしているにも関わらず、
g_ADCBufferバッファ変数長さを32bitにしてしまい、
AD変換結果のバッファを破壊するというバグに気付かず、
意図したFFT結果を得るのに時間が掛かってしまいました...
HalfWordではuint16_t g_ADCBufferが正解です。


FFT処理をしているとSTM32F303K8では
RAMサイズが16kB(実質12kB)のため、
STM32F303CCなどの32kBや40kBの大容量なRAMが欲しくなります。
一方でRAMサイズが大きくなるとピン数が48ピンになるため、
扱いやピン配列が面倒になります...
32ピンでRAMサイズが大きなものが出てほしいなあと思う限りです。

posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする

2016年11月20日

タイマ割り込みADCサンプリング

今回はSTM32でのタイマ割り込みでADCのサンプリングを行う際の設定について紹介します。


環境はこれまで同様、
・STM32F303K8
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(F3_1.6.0)
です。
 

STM32CubeMXでADCは下記のように設定します。

ADsetting.png

今回はTimer2をトリガにしてADCサンプリングを行います。
ポイントはContinuousConvModeをDisableにする点。

最初、通常のADCサンプリング同様に
ContinuousConvModeをENABLEにしていました。
この状態ではタイマによらず、
ADC側のタイミングで連続して変換されてしまい、
意図したタイミングになりませんでした。



STM32CubeMXでTimer2は下記のように設定します。

timer2.png

タイマのPrescalerとPeriodは適当に設定して
後から下記のようにコード上で書き換えます。

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = (SystemCoreClock / 1000000) - 1;  // 1us Tick
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 10-1; // 10us (100kHz)
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

上記の設定でPrescaleをクロックに応じた式にすることで
Periodをusとしてそのまま直感的に定義することが可能です。
ただ、秒単位の場合は変数の範囲を超えるため、
必要に応じてClockDivisioやPrescalerの変更が必要です。






posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする

2016年11月14日

DSP処理前設定2

今回はSTM32でのDSP処理の前設定2について紹介します。

環境はこれまで同様、
・STM32F303K8
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(F3_1.6.0)
です。

前回紹介したDSP処理の前設定だけではsqrtなどの指数関数がコード上にあると
下記のようなエラー発生し、コンパイルできないという不具合に遭遇しました。


undefined reference to `__errno'
collect2.exe: error: ld returned 1 exit status
make: *** [*XXXX.elf] Error 1


ハードFPUで指数関数等を処理するためにgccの引数を追記します。

Eclipseのプロジェクトから
「C/C++ Build」→「Settings」→「ToolSettings」→「MCU GCC Compiler」
「Command」欄を「gcc -fno-math-errno」へ変更します。


FFT_Setting2222.png



「OK」で設定を保存してください。



これで、指数関数がハードウェアFPUで使用可能になります。

    arm_sqrt_f32(a,&b);
    b=sqrtf(a);

  ※sqrt()はソフトFPUとなるため、使いません。



また、以前紹介したようにfloatでprintfするためには下記も追記してください。
Eclipseのプロジェクトから
「C/C++ Build」→「Settings」→「ToolSettings」
→「MCU GCC Linker」→「Miscellaneous」
「Linker flags」の欄を
「-specs=nosys.specs -specs=nano.specs -u _printf_float」へ変更します。


posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする