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%以下に簡単に追い込める。
速度を上げていってもデータが化けることがない。
便利になったもんだ。
コード
#ifndef UART6
#define UART6
extern void uart6_init(void);
extern void uart6_send(void);
#endif
#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
}
}