2016年12月03日

GPIOを介したSDカード読み書き


今回はSTM32でのGPIOでSDカードの読み書きについて紹介します。
特にSTM32F303K8等ではSPIが1つしかなく、SDIOもないため、
CubeMXで自動生成されず、
SDカードの読み書きに苦労するところです。

ChanさんのFatFSモジュールを使用して
STM32のGPIOによるSDカード読み書きを紹介します。

読み書きの速度に期待はできないものの、
空きのGPIOで自由に使用可能なため、
汎用性は非常に高いです。

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


CubeMXでSDカードで使用するGPIOを定義します。
最低限はCS,CLK、MOSI、MISOの4つです。
MISOのみInput、他はOutputで定義します。
プルアップ及びハイスピード設定を有効にします。

今回は
CS:PA11(D10)
CLK:PB5(D11)
MOSI:PB4(D12)
MISO:PB3(D13)
で定義しました。

※Chanさんのモジュールを使用するため、
  CubeMX自体のFatFSはチェックをいれません。


下の方にある「サンプルプロジェクト」をダウンロードします。

ffsampleフォルダ内のgenericフォルダのコードを使用します。
Workspace内のIncとSrcにそれぞれ下記のファイルを入れます。
diskio.h、ff.c、ff.h、ffconf.h、interger.h、sdmm.c


STM32ですぐ使えるように設定変更します
◆ffconf.h
 //RTC無効化
 #define _FS_NORTC    1
 //LFN無効化
 #define    _USE_LFN    0

◆sdmm.c

#define DO_INIT()                             
#define DO            HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6)

#define DI_INIT()                              
#define DI_H()        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_SET)
#define DI_L()        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_RESET)

#define CK_INIT()                              
#define CK_H()        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET)
#define CK_L()        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET)

#define CS_INIT()                          
#define CS_H()        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET)
#define CS_L()        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET)



dly_us (UINT n)関数内のPINB;をコメントアウト

select (void)関数が名前が重複するため、
適当な他の名前に置き換え、sdmm.c内のselectを置き換えた名前に2か所変更。

#include <avr/io.h> をコメントアウトして#include "stm32f3xx_hal.h" に変更。


あとは、main.c内で
#include "ff.h"をインクルード

FATFS FatFs;      
FIL Fil;           
を変数定義

while(1)の中で下記を実行。


     int bw;
           if(f_mount(&FatFs, "", 1)==FR_OK)printf("Mount OK!! \n");
           else {
               printf("Mount Fail!! \n");
               while(1);
           }
                  
           if (f_open(&Fil, "newfile.txt", FA_WRITE | FA_CREATE_ALWAYS) == FR_OK) {  

                   f_write(&Fil, "It works!!!!!!!\r\n", 16, &bw);   
                   f_close(&Fil);                               

                   if(bw==16)printf("Write OK!! \n");
                   while(1);

            }
      else{
                   printf("Open Fail!! \n");
                   while(1);
            }
これでGPIOを使って書き込みができます。
GPIOの書き込みは思った以上に簡単にできました。



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) | ナレッジ | このブログの読者になる | 更新情報をチェックする

2016年11月09日

DSP処理の前設定

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

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


STMのサイトからSTM32Cube_FW_F3_V1.6.0をダウンロードすると
いくつかサンプルコードが入っています。

その中にM4コア内臓のDSP処理を使用したFFTのサンプルがあります。
STM32Cube_FW_F3_V1.6.0\Drivers\CMSIS\DSP_Lib\Examples\arm_fft_bin_example\ARM


・FFT処理するためのサンプルデータ
 arm_fft_bin_data.c
・FFT処理のサンプルコード
 arm_fft_bin_example_f32.c


上記のコードをそのままCubeMXで生成したコード群に
そのまま組込んでもコンパイルが通らないため、
対処方法をご紹介します。

@
#include "stm32f3xx_hal.h"の次に
#define ARM_MATH_CM4
を追加。
F3のM4コアの使用を定義。
この定義を忘れるとコアの種類が分からない旨のコンパイルエラーが出る。
#error "Define according the used Cortex core ARM_MATH_CM7,
ARM_MATH_CM4, ARM_MATH_CM3, ARM_MATH_CM0PLUS or ARM_MATH_CM0"

A
#include "arm_math.h"
#include "arm_const_structs.h"
をインクルード。
コアの定義の後に上記のインクルードをする。


B
STM32Cube_FW_F3_V1.6.0\Drivers\CMSIS\Lib\ARM
内の
arm_cortexM4lf_math.lib
をプロジェクトフォルダ直下へ配置する。

 arm_cortexM4b_math.lib
 arm_cortexM4bf_math.lib
 arm_cortexM4l_math.lib
 arm_cortexM4lf_math.lib
から必要に応じて選択。
little endian, fpu, m4の場合で
arm_cortexM4lf_math.lib


C
プロジェクトのプロパティーで「C/C++Build」の設定内の
Linker、LibrariesでDSP処理のライブラリを追加する。

Librariesに下記を追加。ポイントは「:」を頭につける点。
:arm_cortexM4lf_math.lib

Library search pathにワークスペース下のプロジェクトフォルダを選択。
自動的に下記になる。
"${workspace_loc:/${ProjName}}"

この設定を忘れると
undefined reference to `arm_cfft_f32'
cannot find -larm_cortexM4lf_math.lib
なエラーが出る

FFT_Setting.png


以上でコンパイルが通りました。

DSP処理のFFT関数の使い方などは後ほどご紹介します。
posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする