LoginSignup
1
2

More than 3 years have passed since last update.

STM32F411をHALを使わずにUART送信

Posted at

Indroduction

STM32F4のUSART6を使って、UART通信を行うためのBSWを作った。
HALを使ったサンプルはいっぱい転がっているが、趣味であるならばベアメタルなやり方も悪くはない。

目的

アプリ側が任意のタイミングでコールすることバッファに蓄積されたデータをDMA経由でUSART6に転送しUART送信する機能を提供する。
最終的な形はアプリ側が5msec周期くらいでコールして周期送信させたい。

アプリコール関数
uart6_send();
これをコールすることでリングバッファから送信用バッファにデータをコピーしてDMAに投げ込む(送信開始)

前提条件

マイコンボード Nucleo-F411RE
クロック設定 APB2:80MHz
開発環境 CrossWorks for ARM

ポイント

<考え方>
アプローチとしては2つやり方があると思う。
1 DMAをサーキュラモードにして、固定のバッファの中身を送り続ける。
 →「いつも同じ長さ」のメッセージでいいなら多分これが一番楽。
2 任意タイミングで送信用の関数をコールしたときに、DMAに送信長をセットしてUSARTを起動する。
 →DMA送信完了割り込みでDMAを禁止→USARTの送信完了でUSARTを止める。
  →今回は送信長は可変にしたいので、今回はこっちを使った。

<ボーレートの設定>
昔のH8とかのクセが抜けない悪癖で若干悩んだ.
どーもSTM32と言うマイコンはUSARTのボーレートジェネレータは小数点以下もイケるらしい。
この部分。
USART6->BRR |= (43<<4) + 7; //baudrate is 115.2kbps(APB2 80MHz)

(43<<4)が整数部
+ 7が小数部。
要はこのレジスタの下位4bitは固定小数として扱える。
これのおかげでボーレートジェネレータの誤差は0.1%以下に簡単に追い込める。
速度を上げていってもデータが化けることがない。
便利になったもんだ。

コード

uart.h
#ifndef UART6
#define UART6

extern void uart6_init(void);
extern void uart6_send(void);
#endif
uart.c
#include "stm32f4xx.h"
#include "uart6.h"
#include "var.h"
#include "cont.h"
#include "typedef.h"

static char TX_BUFF[UART_BUFF_SIZE];            //Temp Buffer for TX

void uart6_init(void)
{
    RCC->APB2ENR |= RCC_APB2ENR_USART6EN;       //USART clk start(80MHz)

    DMA2_Stream7 -> CR |=  DMA_SxCR_CHSEL_0;        //DMA2 Stream7 is USART6_TX
    DMA2_Stream7 -> CR &= ~DMA_SxCR_CHSEL_1;
    DMA2_Stream7 -> CR |=  DMA_SxCR_CHSEL_2;
    DMA2_Stream7 -> NDTR = 0;
    DMA2_Stream7 -> PAR = (uint32)&(USART6->DR);    //peripheral register is USART6_DR
    DMA2_Stream7 -> M0AR = (uint32)&TX_BUFF;        //Memory address set

    DMA2_Stream7 -> CR |=  DMA_SxCR_DIR_0;      //DMA2 Stream7 Memory to Peripheral
    DMA2_Stream7 -> CR &= ~DMA_SxCR_DIR_1;
    DMA2_Stream7 -> CR &= ~DMA_SxCR_CIRC;       //Circular Mode is Disable

    DMA2_Stream7 -> CR &= ~DMA_SxCR_PSIZE_0;        //Peripheral Data Size is 8byte
    DMA2_Stream7 -> CR &= ~DMA_SxCR_PSIZE_1;
    DMA2_Stream7 -> CR &= ~DMA_SxCR_PINC;       //Peripheral address Pointer is fixed

    DMA2_Stream7 -> CR &= ~DMA_SxCR_MSIZE_0;        //Memory Data Size is 8byte
    DMA2_Stream7 -> CR &= ~DMA_SxCR_MSIZE_1;
    DMA2_Stream7 -> CR |=  DMA_SxCR_MINC;       //Memory address Pointer is incremented

    DMA2_Stream7 -> CR |=  DMA_SxCR_TCIE;       //DMA Transfer complete interrupt is enable
    DMA2_Stream7 -> CR &= ~DMA_SxCR_PFCTRL;     //DMA is the flow Controller

    USART6->CR2 &= ~USART_CR2_STOP_0;           //USART stop bits = 1
    USART6->CR2 &= ~USART_CR2_STOP_1;

    USART6->CR1 &= ~USART_CR1_OVER8;            //USART oversampling by 16
    USART6->CR1 &= ~USART_CR1_M;            //Startbit 1, Databits 8, Stopbit n,
    USART6->CR1 &= ~USART_CR1_PCE;          //Parity control is disabled

    USART6->BRR |= (43<<4) + 7;             //baudrate is 115.2kbps(APB2 80MHz)
    USART6->CR3 |=  USART_CR3_DMAT;         //USART DMA transmit enable 
    USART6->CR1 |=  USART_CR1_UE;           //USART is enable
    USART6->CR1 |=  USART_CR1_RE;           //USART receiver is enable
    USART6->CR1 |=  USART_CR1_TCIE;         //Interrupt -> Transmit Complete

    NVIC_SetPriority(USART6_IRQn,9);            //USART6 Interrupt Priority is 9
    NVIC_EnableIRQ(USART6_IRQn);
    NVIC_SetPriority(DMA2_Stream7_IRQn,10);     //Interrupt Priority is 10
    NVIC_EnableIRQ(DMA2_Stream7_IRQn);
}

void DMA2_Stream7_IRQHandler(void)
{
    if((DMA2->HISR & DMA_HISR_TCIF7)==DMA_HISR_TCIF7)
    {
    DMA2_Stream7 -> CR &= ~DMA_SxCR_EN;     //DMA is Disable
    DMA2->HIFCR |= DMA_HIFCR_CTCIF7;        //Interrupt Flag is Clear
    }
}

void USART6_IRQHandler(void)
{
    if(USART6->SR & USART_SR_TC)
    {
    USART6->CR1 &=  ~USART_CR1_TE;          //USART transmitter is Disable
    USART6->SR &= ~USART_SR_TC;         //Interrupt Flag is Clear
    }
}

void uart6_send(void)
{
    int8 length;
    length = ring_buf_deq(&mTX_WORK,TX_BUFF);       //From Work memory -> Send memory

    //Data is available?
    if(length >= 1)
    {
    DMA2_Stream7 -> NDTR = length;          //Set Transmit length
    DMA2_Stream7 -> CR |=  DMA_SxCR_EN;     //DMA is Enable
    USART6->CR1 |=  USART_CR1_TE;           //USART transmitter is Enable
    }
}
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2