2014年3月6日木曜日

STM32F1でUSARTの受信割り込み

STM32F103でUSART1の受信割り込み
今回は単純に受信バッファを実装するだけです
過去のエントリのコードはかなり細切れなので、最後にまとめてコードを貼り付けておきます


まず初期化ですが、前回の送信割り込みの時にNVICを設定したので特に大きく変化する部分はありません
ただし受信割り込みの有効化はしておきます
    USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

割り込みハンドラの前に
char USART1rxBuff[500];
volatile uint16_t USART1rxSet = 0, USART1rxGet = 0;
という計3個の変数を追加しておきます

そして割り込みハンドラに
    if (USART_GetITStatus(USART1, USART_IT_RXNE)) {
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);

        USART1rxBuff[USART1rxSet] = USART_ReceiveData(USART1);
        if (++USART1rxSet == sizeof(USART1rxBuff))
        { USART1txSet = 0; }

        if (USART1rxSet == USART1rxGet) {
            if (++USART1rxGet == sizeof(USART1rxBuff))
            { USART1rxGet = 0; }
        }

        return;
    }
という処理を追加します

最後にgetch関数を
int USART1_getch(void) {
    char ch;

    if (USART1rxSet == USART1rxGet)
    { return(-1); }

    ch = USART1rxBuff[USART1rxGet];

    if (++USART1rxGet == sizeof(USART1rxBuff))
    { USART1rxGet = 0; }

    return(ch);
}
のように書き換えます
これで受信バッファの出来上がりです


続いて1行受信の関数です
const char *USART1_getline(void) {
    static char USART1rxLine[100];
    static uint16_t USART1liSet = 0;

    int ch = USART1_getch();
    if (ch == -1) { return(0); }

    if (USART1liSet && (ch == '\n' || ch == '\r')) {
        USART1rxLine[USART1liSet] = '\0';
        USART1liSet = 0;
        return(USART1rxLine);
    }

    USART1rxLine[USART1liSet++] = ch;
    if (USART1liSet == sizeof(USART1rxLine) - 1) {
        USART1rxLine[USART1liSet] = '\0';
        USART1liSet = 0;
        return(USART1rxLine);
    }

    return(0);
}
この関数の使い方は、適切なタイミングでこの関数を呼び、戻り値を受け取ります
戻り値のポインタが0(NULL)の場合は1行分が入力されていません
ポインタが0でない場合はそれが1行分の文字列へのポインタとなります
1行が読み取られた後に再度関数を呼ぶと文字列バッファは新しい文字を読み込むため、データが破壊されます
関数を呼び出すサイクル以上に長い間文字列が必要な場合はstrcpy等を使って他のバッファに移動しておく必要があります

なお、この関数を使用する場合は他の箇所でUSART1_getch();を呼んではいけません
もしも呼んだ場合はその時点で行バッファに読み込まれていない最初の文字が欠損することになります
またこの関数では1行バッファ(-1)分の文字数以上が1行に入力された場合は、その時点でバッファに入っている文字列を1行として返し、それ以降は新しい行として読み取られます

それと、デバッグ環境で使用したコンソールがEnterを押すと'\r'だけを送信する という環境だったため、'\n'の環境や"\r\n"の環境での動作確認ができていません
おそらく動作すると思いますが、その点は注意してください


最後に、コードを最初から最後までまとめて貼り付けておきます



#include "stm32f10x_conf.h"
#include "xprintf.h"

typedef struct { uint32_t Second; uint16_t Milli; } Seconds;

void RCC_init(void);
void GPIO_init(void);
void USART_init(void);
void USART1_putch(char ch);
void USART1_puts(const char *str);
int USART1_getch(void);
const char *USART1_getline(void);
void USART1txDelay(void);
void TIM_init(void);
void TIM2_IRQHandler(void);
int GetDiff(const Seconds *sec1, const Seconds *sec2, Seconds *sec);
void USART1_IRQHandler(void);
int strlen(const char *s);

volatile Seconds sec;

void Delay(volatile unsigned long delay) { while(delay) delay--; }

int main(void) {
    SystemInit();
    NVIC_SetVectorTable(0x3000, 0);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

    RCC_init();
    GPIO_init();
    USART_init();
    TIM_init();

    xprintf("\nHello World!! %s %s : %s %d\n"
        __DATE__, __TIME__, __FILE__, __LINE__);

    while(1){
        GPIO_WriteBit(GPIOD, GPIO_Pin_4, 
            !GPIO_ReadOutputDataBit(GPIOD, GPIO_Pin_4));
        Delay(500000);

        const char *ptr = USART1_getline();
        if (ptr) { xprintf("line:%02d:%s\n", strlen(ptr), ptr); }
    }
}

void RCC_init(void) {
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,   ENABLE); AFIO->MAPR = _BV(26);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,  ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,  ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,   ENABLE);
}

void GPIO_init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    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_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void USART_init(void) {
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    USART_StructInit(&USART_InitStructure);
    USART_InitStructure.USART_BaudRate = 115200;
    USART_Init(USART1, &USART_InitStructure);

    USART_Cmd(USART1, ENABLE);
    xdev_out(USART1_putch);

    /* *** */
    USART_ClearITPendingBit(USART1, USART_IT_TXE);
    USART_ClearITPendingBit(USART1, USART_IT_RXNE);

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}

char USART1txBuff[500];
volatile uint16_t USART1txSet = 0, USART1txGet = 0;
char USART1rxBuff[500];
volatile uint16_t USART1rxSet = 0, USART1rxGet = 0;
void USART1_IRQHandler(void) {
    if (USART_GetITStatus(USART1, USART_IT_TXE)) {
        USART_ClearITPendingBit(USART1, USART_IT_TXE);

        USART_SendData(USART1, USART1txBuff[USART1txGet]);
        if (++USART1txGet == sizeof(USART1txBuff))
        { USART1txGet = 0; }

        if (USART1txGet == USART1txSet)
        { USART_ITConfig(USART1, USART_IT_TXE, DISABLE); }

        return;
    }
    if (USART_GetITStatus(USART1, USART_IT_RXNE)) {
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);

        USART1rxBuff[USART1rxSet] = USART_ReceiveData(USART1);
        if (++USART1rxSet == sizeof(USART1rxBuff))
        { USART1txSet = 0; }

        if (USART1rxSet == USART1rxGet) {
            if (++USART1rxGet == sizeof(USART1rxBuff))
            { USART1rxGet = 0; }
        }

        return;
    }
}

void USART1_putch(char ch) {
    USART1txBuff[USART1txSet] = ch;
    if (++USART1txSet == sizeof(USART1txBuff))
    { USART1txSet = 0; }

    if (USART1txSet == USART1txGet) {
        if (++USART1txGet == sizeof(USART1txBuff))
        { USART1txGet = 0; }
    }

    USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
}

void USART1_puts(const char *str)
{ while (*str) { USART1_putch(*str++); } }

int USART1_getch(void) {
    char ch;

    if (USART1rxSet == USART1rxGet)
    { return(-1); }

    ch = USART1rxBuff[USART1rxGet];

    if (++USART1rxGet == sizeof(USART1rxBuff))
    { USART1rxGet = 0; }

    return(ch);
}

const char *USART1_getline(void) {
    static char USART1rxLine[100];
    static uint16_t USART1liSet = 0;

    int ch = USART1_getch();
    if (ch == -1) { return(0); }

    if (USART1liSet && (ch == '\n' || ch == '\r')) {
        USART1rxLine[USART1liSet] = '\0';
        USART1liSet = 0;
        return(USART1rxLine);
    }

    USART1rxLine[USART1liSet++] = ch;
    if (USART1liSet == sizeof(USART1rxLine) - 1) {
        USART1rxLine[USART1liSet] = '\0';
        USART1liSet = 0;
        return(USART1rxLine);
    }

    return(0);
}

void USART1txDelay(void)
{ while (USART1txSet != USART1txGet); }

void TIM_init(void) {
    TIM_TimeBaseInitTypeDef TIM_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    TIM_InitStructure.TIM_Period        = 720 - 1;
    TIM_InitStructure.TIM_Prescaler        = 100 - 1;
    TIM_InitStructure.TIM_ClockDivision    = TIM_CKD_DIV1;
    TIM_InitStructure.TIM_CounterMode    = TIM_CounterMode_Down;
    
    TIM_TimeBaseInit(TIM2, &TIM_InitStructure);
    /* *** */
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority  = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    
    NVIC_Init(&NVIC_InitStructure);
    /* *** */
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

    TIM_Cmd(TIM2, ENABLE);
}

void TIM2_IRQHandler(void) {
    if (TIM_GetITStatus(TIM2, TIM_IT_Update)) {

        sec.Milli++;
        if (sec.Milli >= 1000) {
            sec.Milli -= 1000;
            sec.Second++;
        }

        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}

int GetDiff(const Seconds *sec1, const Seconds *sec2, Seconds *sec) {
    sec->Second = sec->Milli = 0;

    if (sec1->Second == sec2->Second) {
        if (sec1->Milli == sec2->Milli) {
            return(0);
        } else if (sec1->Milli > sec2->Milli) {
            sec->Milli = sec1->Milli - sec2->Milli;
            return(1);
        } else {
            sec->Milli = sec2->Milli - sec1->Milli;
            return(-1);
        }
    } else if (sec1->Second > sec2->Second) {
        sec->Second = sec1->Second - sec2->Second;
        if (sec1->Milli > sec2->Milli) {
            sec->Milli = sec1->Milli - sec2->Milli;
        } else {
            sec->Milli = sec2->Milli - sec1->Milli;
            sec->Second--;
            sec->Milli = 1000 - sec->Milli;
        }
        return(1);
    } else {
        sec->Second = sec2->Second - sec1->Second;
        if (sec1->Milli > sec2->Milli) {
            sec->Milli = sec1->Milli - sec2->Milli;
            sec->Second--;
            sec->Milli = 1000 - sec->Milli;
        } else {
            sec->Milli = sec2->Milli - sec1->Milli;
        }
        return(-1);
    }
}

int strlen(const char *s) {
    int len = 0;

    while (*s++) { len++; }

    return(len);
}


0 件のコメント:

コメントを投稿