2014年7月28日月曜日

RAMを交換してみた

メインマシンのRAMを交換してみました
このマシンは組むときに最低構成で作って、後から更新していく という方針で組んだものです
RAMは8GBありましたが、ちょっと足りないなぁという感じだったので、思い切ってTranscendの16GBキット(2x8GB)を2組買ってみました
今回買ったもの(AA):Transcend JetRam デスクトップPC用増設メモリ PC3-12800(DDR3-1600) 16GB KIT(8GB×2) 永久保証 JM1600KLH-16GK

8GBからの増設ではあまり大きな変化はありません
ただアプリケーションの切り替えでHDDからの読み込みが発生しないので、さくさく切替できます

RAM増設後のメモリ使用量は10GB程度で、8GBに入らない2GB分はHDDに退避されます
そのためこの2GB分に入っているデータを使用するアプリケーションを開く際には
1) RAMのデータをHDDに退避
2) RAMの空いた領域に必要なデータをHDDから戻す
3) アプリケーションの処理を開始する
という動作が行われます
1と2が無駄な動作で、しかも非常に時間がかかります


とりあえずRAMは32GBになって20GBくらい余裕があるので、8GBだけRAMDISKにしてみました
上がHDD 下がRAMDISKです

シーケンシャルライトは43倍 シーケンシャルリードは0.9倍
512ランダムリードは120倍 512ライトは1.1倍
4kランダムリードは1000倍 4kライトは0.6倍
という感じで、ランダムリードは爆速 ライトはほとんど変わらず という結果です

データ処理の元データとかをRAMDISKに入れると早いかもしれません
といっても、等高線データとか40GBくらいあるのでメモリは64GBくらい無いと足りないですがw


正直、RAMDISKって微妙な気がしてきました
Chromeのキャッシュでも突っ込んでおこうかと思います

2014年7月25日金曜日

STM32F1でPWMのキャプチャ

STM32F1のTIMでPWMをキャプチャする方法
今回はTIM2を使いました

日本語で調べた感じあんまり解説が無かったので備忘録兼ね

    {
        GPIO_InitTypeDef GPIO_InitStructure;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
    }
    
    {
        TIM_TimeBaseInitTypeDef TIM_InitStructure;
        TIM_TimeBaseStructInit(&TIM_InitStructure);
        TIM_InitStructure.TIM_Prescaler        = 72 - 1;
        TIM_InitStructure.TIM_CounterMode    = TIM_CounterMode_Up;
        TIM_InitStructure.TIM_Period        = 0xFFFF;
        TIM_InitStructure.TIM_ClockDivision    = TIM_CKD_DIV1;
        //TIM_InitStructure.TIM_RepetitionCounter    = ;
        TIM_DeInit(TIM2);
        TIM_TimeBaseInit(TIM2, &TIM_InitStructure);
    }
    
    {
        TIM2->CCMR1 = (TIM2->CCMR1 & 0xFFFC) | (( 0b01 <<  0) & 0x0003);
        TIM2->CCMR1 = (TIM2->CCMR1 & 0xFF0F) | ((    6 <<  4) & 0x00F0);
        TIM2->CCER  = (TIM2->CCER  & 0xFFFD) | ((    0 <<  1) & 0x0002);
        TIM2->CCMR1 = (TIM2->CCMR1 & 0xFFF3) | ((    0 <<  2) & 0x000C);
    }
    {
        TIM2->CCMR1 = (TIM2->CCMR1 & 0xFCFF) | (( 0b10 <<  8) & 0x0300);
        TIM2->CCER  = (TIM2->CCER  & 0xFFDF) | ((    1 <<  5) & 0x0020);
        TIM2->SMCR  = (TIM2->SMCR  & 0xFF8F) | ((0b101 <<  4) & 0x0070);
        TIM2->SMCR  = (TIM2->SMCR  & 0xFFF8) | ((0b100 <<  0) & 0x0007);
    }
    TIM2->CCER  = (TIM2->CCER  & 0xFFFE) | ((    1 <<  0) & 0x0001);
    TIM2->CCER  = (TIM2->CCER  & 0xFFEF) | ((    1 <<  4) & 0x0010);
    
    TIM_Cmd(TIM2, ENABLE);

最初にGPIOを初期化 TIM2 Ch1が接続されているPA0をIN PullDownで初期化します

次にタイマモジュールの初期化
今回は1マイクロ秒単位で計測したかったのでプリスケーラに72を入れています
Periodは正直何の設定かわからないです
おそらく計測できる最大の周期を設定するんではないかと
そしてキャプチャモジュールの初期化
本来はStdPeriphLibを使うべきですがRM0008を読みながら実装したため、直接ビット操作しています
1個目のブロックで立ち上がりエッジを 2個目のブロックで立ち下がりエッジを検出するための設定をしています
立ち下がりエッジの方はフィルタの設定等していないです 一応動いてますが
そしてキャプチャ2個をイネーブルにしてからTIM自体をイネーブルにしています
コレで計測ができます

値を読み出すには
    if (TIM_GetFlagStatus(TIM2, TIM_IT_CC1)) {
        uint16_t val = TIM2->CCR1;
        xprintf("CCR1:%d\n", val);
    }
    if (TIM_GetFlagStatus(TIM2, TIM_IT_CC2)) {
        uint16_t val = TIM2->CCR2;
        xprintf("CCR2:%d\n", val);

    }
みたいな感じでできます

今回は5msec周期で25%のパルスを入れてみました
その結果CCR1が5000 CCR2が1250 という値が読み出されました
分解能1usecで初期化しているため、5msecは5000として読み出され、パルス幅はその25%なので1250と、正常に動作しているようです

1chのPWMを読む場合、CCRはあと2本余っているので、ココでもう1本のPWMを読むとか、PWMを2本出力できるかは不明です

まずはPeriphLibで書き換えてから、他の機能を試してみようと思います
しかしSTMのTIMってレジスタ多すぎ 80バイトって…


追記:2014/07/26
StdPeriphLibを使って初期化する方法
    {
        GPIO_InitTypeDef GPIO_InitStructure;
        GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
    }
 
    {
        TIM_TimeBaseInitTypeDef TIM_InitStructure;
        TIM_TimeBaseStructInit(&TIM_InitStructure);
        TIM_InitStructure.TIM_Prescaler     = 72 - 1;
        TIM_InitStructure.TIM_CounterMode   = TIM_CounterMode_Up;
        TIM_InitStructure.TIM_Period        = 0xFFFF;
        TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
        //TIM_InitStructure.TIM_RepetitionCounter = ; /* TIM1 or TIM8 */
        TIM_DeInit(TIM2);
        TIM_TimeBaseInit(TIM2, &TIM_InitStructure);
    }

    {
        TIM_ICInitTypeDef TIM_ICInitStructure;
        TIM_ICInitStructure.TIM_Channel     = TIM_Channel_1;
        TIM_ICInitStructure.TIM_ICPolarity  = TIM_ICPolarity_Rising;
        TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
        TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
        TIM_ICInitStructure.TIM_ICFilter    = 5;
        TIM_PWMIConfig(TIM2, &TIM_ICInitStructure);
    }

    TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1);
    TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);

    TIM_Cmd(TIM2, ENABLE);

やはりStdPeriphLibはわかりやすいです
ビット単位で設定とか何やってるかわからないですし
次は残りのChannelをどうするか考えてみます


追記:2014/07/26(2回目)
PWM入力とPWM出力を同時に行うことはできないようです
タイミングチャートを見ると
パルスの立ち上がりでタイマのカウンタがクリアされるようです
そのため他にタイミングソースを提供することができません
という理由によりPWM入力は1本だけ、PWM出力も不可 ということになるようです

パルス幅を1個計測するのにTIMモジュールが1本必要なので、比較的高コストです
外部のセンサでUART接続よりパルス出力のほうが低コストかな と思ってましたが 全然そんなことなかったです

他の方法だとADCは3本しか無いですが、順次切り替えて10本以上測定できるので、こっちのほうが低コストかもしれません

2014年7月11日金曜日

STM32F4DISCOVERYのピン

1回位はSTM32F4DISCOVERYでなにかつくろうか と思ってピンアサインを考えています
しかしこの基板はオンボードで色々乗っているので、注意しないと面倒なことになってしまいます
ということでまずは調べてみました


黄色がオンボードのアサインです

まずSTBee等と違い連番ででているわけではありません
また結構オンボードで使用されています

機能等を考えつつ上手くはめ込んでいこうと思います
(その前に何を作るか考えないとな…)

2014年7月4日金曜日

STM32F1 自身をリセットする

CMSISのcore_cm3.hには NVIC_SystemReset という関数があります
というかinline関数がヘッダファイルに書いてあります

この関数 読んで字の如し、システムをリセットすることができます

なおこの機能はCortex-M3レベルのハードウェア機能なので、STMicroのRM0008には書かれていません

例えばUSARTで"reset"が送られてきたらこの関数を呼ぶ という感じにしておけばPCからリセットできます

STBeeの場合、GPIOA0とGNDの間に0.1uF程度のキャパシタを付けて、リセットする前にGPIOA0をOut_PPで初期化してSetBitsで充電してからリセットするとDFUを起動できます
(STBeeMiniはDFU起動スイッチのモードが逆なので注意 あとポートも違う)

DFUをリセットするのは手動で行うか、電源を切断してリセットする必要があるので、あまり使い勝手がいいとは言えませんが、DFU起動スイッチが奥まった場所にある場合などは使える方法かもしれません?

追記:2014/07/05
0.1uFではリセットされなくなってきました
当初は正常に動いていましたが、いつの間にか…
0.1uFではプルダウン抵抗に対して弱すぎるようです
試しに47uFを接続したところ、正常に動作しているようです

2014年7月3日木曜日

STM32F1のUSARTとDMA

STM32F1のUSARTとDMAを組み合わせてみました


今回は受信をDMAで転送し、送信はソフトウェアループで行いました


プログラムですが

まずFIFOとしてメモリを確保します
unsigned char ConsoleRxBuffer[100];

次に初期化です
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
DMA_InitTypeDef DMA_InitStructure;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);

USART_InitStructure.USART_BaudRate    = 115200;
USART_InitStructure.USART_WordLength  = USART_WordLength_8b;
USART_InitStructure.USART_StopBits    = USART_StopBits_1;
USART_InitStructure.USART_Parity      = USART_Parity_No;
USART_InitStructure.USART_Mode        = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART2, &USART_InitStructure);

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART2->DR);
DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)(ConsoleRxBuffer);
DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize         = sizeof(ConsoleRxBuffer) / sizeof(ConsoleRxBuffer[0]);
DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority           = DMA_Priority_Medium;
DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel6, ENABLE);

USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);
USART_Cmd(USART2, ENABLE);

GPIO USART DMAを初期化します
RCCは予め初期化しておく必要があります
このコードで使用するのはDMA1 USART2 GPIOAです

今回は受信割り込み等は使用しないため、NVICの初期化は不要です

上記3種類の初期化が終了した段階でUSARTの送信が可能であり、データを受信した場合はFIFOに追加されます


最後にFIFOの解析です
const unsigned char *ConsoleInputStr(void) {
    static unsigned char strBuffer[50];
    static uint16_t BufferPos = 0, FIFOpos = 0;
    uint16_t now = (sizeof(ConsoleRxBuffer) / sizeof(ConsoleRxBuffer[0])) - DMA_GetCurrDataCounter(DMA1_Channel6);

    while (FIFOpos != now) {
        char ch = ConsoleRxBuffer[FIFOpos++];
        if (FIFOpos == (sizeof(ConsoleRxBuffer) / sizeof(ConsoleRxBuffer[0]))) { FIFOpos = 0; }
       
        if (ch >= ' ' && ch <= '~') {
            strBuffer[BufferPos++] = ch;
        } else if (ch == '\n') {
            strBuffer[BufferPos] = 0;
            BufferPos = 0;
            return(strBuffer);
        }
    }

    return(0);
}
この関数によりFIFOから1行を受信します
FIFOに改行コードが含まれない場合はぬるぽを返し、改行コードを発見した場合は文字列バッファのポインタを返します
文字列は次にこの関数が呼び出されるまで保証されます
今回はバッファオーバーラン対策はしていません


DMAを使用することによりUSARTの受信→バッファへの転送はハードウェアで行われます
そのためある程度高速でデータを送り込まれても(FIFOの容量までは)取りこぼしせずに受信することができます
STM32F1は115.2kbaudや921.6kbaud等の高速な通信ができますが、byte/secが増える毎に割り込みのオーバーヘッドが無視できなくなります
また、下位の割り込みへのタイミング精度や、上位の割り込みによるデータの取りこぼしの問題も出てきます
その点受信をDMAで行うとそれらの懸念を減らすことができます

依然USARTの送信はプログラムループか、ソフトウェア割り込みで行う必要がありますが、送信については任意のタイミングで行えるため、上位の割り込みによる影響などはあまりありません
またUSARTの割り込み優先度を下げることができるので、他の割り込みへの影響も減らせます


ある程度の速度でデータがどんどん流れこんでくる用途 例えばUART接続のGPSモジュール等に使った場合に有利だと思います