Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: STM32F1 DMA USART Посылка произвольной длины
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Qic
Доброго времени суток!
Делаю на stm32f103 приемопередающую часть с обработчиком произвольных команд для посылок произвольной длины.
Сейчас код выглядит так (самое важное):
CODE
volatile uint8_t usart1_rx_buff[255];
volatile int32_t usart1_rx_buff_lenght = 0;
volatile int32_t usart1_rx_data_ready = 0;
volatile uint8_t usart1_tx_buff[255];
volatile int32_t usart1_tx_buff_lenght = 0;

void USART1_config (void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // Enable clk APB2 -> GPIOA
GPIO_INIT_PIN(GPIOA,9,GPIO_MODE_OUTPUT50_ALT_PUSH_PULL); // USART1 PA9 TX
GPIO_INIT_PIN(GPIOA,10,GPIO_MODE_INPUT_FLOATING); // USART1 PA10 RX

USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd((RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO), ENABLE); //Разрешаем тактирование

USART_InitStructure.USART_BaudRate = 115200;// скорость
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //8 бит данных
USART_InitStructure.USART_StopBits = USART_StopBits_1; //один стоп бит
USART_InitStructure.USART_Parity = USART_Parity_No; //четность - нет
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // управлени потоком - нет
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // разрешаем прием и передачу

USART_Init(USART1, &USART_InitStructure); //инизиализируем

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //канал
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //приоритет
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//приоритет субгруппы
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //включаем канал
NVIC_Init(&NVIC_InitStructure); //инициализируем

USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);

USART_Cmd(USART1, ENABLE);
}

void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) // Receive Data register not empty interrupt
{
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}

if(USART_GetITStatus(USART1, USART_IT_TC) != RESET) // Transmission complete interrupt
{
USART_ClearITPendingBit(USART1, USART_IT_TC);
}

if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) // IDLE
{
USART_ClearITPendingBit(USART1, USART_IT_IDLE); // When DMA receiving get IDLE on UART
USART_ITConfig(USART1, USART_IT_IDLE, DISABLE); // Выключаем прерывание т.к. мы схуято тутже висим постоянно
usart1_rx_data_ready = 1;
}
}


void DMA_for_USART1_TX_config_and_run (uint8_t buff_size)
{
// Config DMA : usart1_tx_buff -> USART1 for 255 bytes
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

DMA_InitTypeDef DMA_InitStruct;
DMA_StructInit(&DMA_InitStruct);
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t) & (USART1->DR);
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t) usart1_tx_buff;
DMA_InitStruct.DMA_BufferSize = buff_size;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_Init(DMA1_Channel4, &DMA_InitStruct);

DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);
NVIC_EnableIRQ(DMA1_Channel4_IRQn);
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);

DMA_Cmd(DMA1_Channel4, ENABLE); // RUN
_delay_us(10);
}

void DMA_for_USART1_RX_config_and_run (void)
{
// Config DMA : USART1 -> usart1_rx_buff for 255 bytes
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

DMA_InitTypeDef DMA_InitStruct;
DMA_StructInit(&DMA_InitStruct);
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t) & (USART1->DR);
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t) usart1_rx_buff;
DMA_InitStruct.DMA_BufferSize = 255;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_Init(DMA1_Channel5, &DMA_InitStruct);

NVIC_EnableIRQ(DMA1_Channel5_IRQn);
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);

DMA_Cmd(DMA1_Channel5, ENABLE); // RUN
}

void DMA1_Channel4_IRQHandler(void) // USART1 TX // Now ONLY transfer complete
{
DMA_Cmd(DMA1_Channel4, DISABLE);
USART_DMACmd(USART1, USART_DMAReq_Tx, DISABLE);

if(DMA1->ISR & DMA_ISR_TCIF4) // Если обмен завершен
{
DMA_ClearITPendingBit(DMA1_IT_TC4);
}

if(DMA1->ISR & DMA_ISR_HTIF4) // Если передана половина буфера
{
DMA_ClearITPendingBit(DMA1_IT_HT4);
}

if(DMA1->ISR & DMA_ISR_TEIF4) // Если произошла ошибка при обмене
{
DMA_ClearITPendingBit(DMA1_IT_TE4);
}

if(DMA1->ISR & DMA_ISR_GIF4) // Если произошла ошибка при обмене
{
DMA_ClearITPendingBit(DMA_ISR_GIF4);
}
}

void DMA1_Channel5_IRQHandler(void) // USART1 RX // Now ONLY transfer complete
{
DMA_Cmd(DMA1_Channel5, DISABLE);
USART_DMACmd(USART1, USART_DMAReq_Rx, DISABLE);

if(DMA1->ISR & DMA_ISR_TCIF5) // Если обмен завершен
{
DMA_ClearITPendingBit(DMA1_IT_TC5);
}

if(DMA1->ISR & DMA_ISR_HTIF5) // Если передана половина буфера
{
DMA_ClearITPendingBit(DMA1_IT_HT5);
}

if(DMA1->ISR & DMA_ISR_TEIF5) // Если произошла ошибка при обмене
{
DMA_ClearITPendingBit(DMA1_IT_TE5);
}

if(DMA1->ISR & DMA_ISR_GIF5) // Если произошла глобальная ошибка
{
DMA_ClearITPendingBit(DMA_ISR_GIF5);
}
}

void Execute_Command (void)
{
if (usart1_rx_data_ready == 1) //
{
usart1_rx_data_ready = 0;
DMA_Cmd(DMA1_Channel5, DISABLE); // Stop DMA
usart1_rx_buff_lenght = 255 - DMA_GetCurrDataCounter(DMA1_Channel5); // Get REMAINING counter for work with usart1_rx_buff
// Data in "usart1_rx_buff" & len of it "usart1_rx_buff_lenght" READY

if (usart1_rx_buff_lenght != 0)
{
// Func FN01 "Echo" ***************************************************************************
*************************
//if ((usart1_rx_buff[0]=='F')&(usart1_rx_buff[1]=='N')&(usart1_rx_buff[2]=='0')&(usart1_rx_buff[3]=='1'))
//{
//GPIOB->BSRR = GPIO_BSRR_BS5; vTaskDelay(100); GPIOB->BSRR = GPIO_BSRR_BR5; vTaskDelay(100); // For DEBUG

usart1_tx_buff_lenght = usart1_rx_buff_lenght;
for (usart1_rx_buff_lenght; usart1_rx_buff_lenght>=0; usart1_rx_buff_lenght--)
{
usart1_tx_buff[usart1_rx_buff_lenght] = usart1_rx_buff[usart1_rx_buff_lenght];
};
DMA_for_USART1_TX_config_and_run(usart1_tx_buff_lenght);
//}
}
// Remain of listening
DMA_for_USART1_RX_config_and_run();
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
}
}

int main(void)
{
System_RCC_config();
GPIO_config();
USART1_config();
DMA_for_USART1_RX_config_and_run();
TIM3_PWM_config_and_run();

xTaskCreate( default_task, ( signed char * ) "default_task", configMINIMAL_STACK_SIZE, NULL, 1, NULL );

__enable_irq ();
vTaskStartScheduler();

while(1){}
}

void default_task ( void *pvParameters )
{
for(;; )
{
vTaskDelay(1);
Execute_Command();
}
vTaskDelete( NULL );
}

В таком виде конкретно функция Execute_Command которая умеет ЭХО делает всё быстро и замечательно. На все скорости ни затыков ничего такого.
Однако когда Я раскомментирую условие "if ((usart1_rx_buff[0]=='F')&(usart1_rx_buff[1]=='N')&(usart1_rx_buff[2]=='0')&(usart1_rx_buff[3]=='1'))" начинается дичь, а именно в случайном порядке пришедший ответ короче с конца на случайное число байт.
А когда Я раскомментирую мигание светодиодиком (for debug) через несколько срабатываний фукнция перестаёт вообще чтолибо делать, т.к. приемный счётчик DMA1_Channel4 возвращает 255.
Причем еще Я использую обработку IDLE флага от USART, перечитав даташит несколько раз всеравно не могу понять - может ли он корректно поймать тишину после байта? Но как видно - работает.

Это мой первый опыт такого конфигурирования и в целом Я не очень хороший программист, возможно кто-нибудь свежим взглядом найдет ошибку и подскажет как сделать лучше.
Надеюсь на вашу помощь.
k155la3
Цитата
Однако когда Я раскомментирую условие "if ((usart1_rx_buff[0]=='F')&(usart1_rx_buff[1]=='N')&(usart1_rx_buff[2]=='0')&(usart1_rx_buff[3]=='1'))" начинается дичь, а именно в случайном порядке пришедший ответ короче с конца на случайное число байт.


для начала в if поменяйте "&" на "&&", т.е. на логический.
Так как по мне - это две большие разницы

ps
чтоб не растягивать нечитаемые по условию ифы, удобно делать так

int log1, log2, logN;

log1 = usart1_rx_buff[0]=='F';
log2 = usart1_rx_buff[2]=='0';
. . . .
logN = log1 && ( !log2 );
if( logN ) .....

(это пример, здесь логика вашей задаче не соответствует - перепишите как Вам надо)
лог. операторы - || && ! (или-и-NOT)
sKWO
попробуйте так
Код
"if ((usart1_rx_buff[0]=='F')&&(usart1_rx_buff[1]=='N')&&(usart1_rx_buff[2]=='0')&&(usart1_rx_buff[3]=='1'))"

Вам нужно не побитное "и" а логическое.

Цитата(k155la3 @ Jul 21 2016, 11:15) *
ps
чтоб не растягивать нечитаемые по условию ифы, удобно делать так

или так
CODE
//поиск подстроки в строке с ограничением по длине поиска
__x_z signed char my_strstr(char *pData, char __flash *pComp, int len)
{
signed char result=-1;

for( unsigned int i=0; i<len; i++)
{
result=1;
for( unsigned int j=0; *(pComp+j); j++)
{
if( ( *(pData+i+j) != *(pComp+j)) || ( !*(pComp+j)) )
{
result=-1;
break;
}
}

if(result == 1)
{
return i; //возвращаем позицию начала подстроки в строке
}
}

return -1;
}


Цитата(Qic @ Jul 21 2016, 10:50) *
Доброго времени суток!
Делаю на stm32f103 приемопередающую часть с обработчиком произвольных команд для посылок произвольной длины.
Сейчас код выглядит так (самое важное):

вот вам пример для вашего контроллера с фифо и дма
хедер
CODE
#pragma once

#define UART_BUFFER_SIZE 64

#define UART1_RX_BUFFER_SIZE 256
#define UART1_TX_BUFFER_SIZE 256
#define UART2_RX_BUFFER_SIZE 128
#define UART2_TX_BUFFER_SIZE 64
#define UART3_RX_BUFFER_SIZE 256
#define UART3_TX_BUFFER_SIZE 256
#define MAX_SERIAL_PORTS 3

// FIXME this is a uart_t really. Move the generic properties into a separate structure (serialPort_t) and update the code to use it
typedef struct {
serialPort_t port;

// FIXME these are uart specific and do not belong in here
DMA_Channel_TypeDef *rxDMAChannel;
DMA_Channel_TypeDef *txDMAChannel;

uint32_t rxDMAIrq;
uint32_t txDMAIrq;

uint32_t rxDMAPos;
bool txDMAEmpty;

USART_TypeDef *USARTx;
} uartPort_t;

extern const struct serialPortVTable uartVTable[];

serialPort_t *uartOpen(USART_TypeDef *USARTx, serialReceiveCallbackPtr callback, uint32_t baudRate, portMode_t mode);

// serialPort API
void uartWrite(serialPort_t *instance, uint8_t ch);
uint8_t uartTotalBytesWaiting(serialPort_t *instance);
uint8_t uartRead(serialPort_t *instance);
void uartSetBaudRate(serialPort_t *s, uint32_t baudRate);
bool isUartTransmitBufferEmpty(serialPort_t *s);

драйвер
CODE
/*
* This file is part of baseflight
* Licensed under GPL V3 or modified DCL - see https://github.com/multiwii/baseflight/blob/master/README.md
*
* DMA UART routines idea lifted from AutoQuad
* Copyright © 2011 Bill Nesbitt
*/
#include "board.h"

static uartPort_t uartPort1;
static uartPort_t uartPort2;
static uartPort_t uartPort3;

// USART1 - Telemetry (RX/TX by DMA)
uartPort_t *serialUSART1(uint32_t baudRate, portMode_t mode)
{
uartPort_t *s;
static volatile uint8_t rx1Buffer[UART1_RX_BUFFER_SIZE];
static volatile uint8_t tx1Buffer[UART1_TX_BUFFER_SIZE];
gpio_config_t gpio;
NVIC_InitTypeDef NVIC_InitStructure;

s = &uartPort1;
s->port.vTable = uartVTable;

s->port.baudRate = baudRate;

s->port.rxBuffer = rx1Buffer;
s->port.txBuffer = tx1Buffer;
s->port.rxBufferSize = UART1_RX_BUFFER_SIZE;
s->port.txBufferSize = UART1_TX_BUFFER_SIZE;

s->rxDMAChannel = DMA1_Channel5;
s->txDMAChannel = DMA1_Channel4;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// USART1_TX PA9
// USART1_RX PA10
gpio.speed = Speed_2MHz;
gpio.pin = Pin_9;
gpio.mode = Mode_AF_PP;
if (mode & MODE_TX)
gpioInit(GPIOA, &gpio);
gpio.pin = Pin_10;
gpio.mode = Mode_IPU;
if (mode & MODE_RX)
gpioInit(GPIOA, &gpio);

// DMA TX Interrupt
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

return s;
}

// USART2 - GPS or Spektrum or ?? (RX + TX by IRQ)
uartPort_t *serialUSART2(uint32_t baudRate, portMode_t mode)
{
uartPort_t *s;
static volatile uint8_t rx2Buffer[UART2_RX_BUFFER_SIZE];
static volatile uint8_t tx2Buffer[UART2_TX_BUFFER_SIZE];
gpio_config_t gpio;
NVIC_InitTypeDef NVIC_InitStructure;

s = &uartPort2;
s->port.vTable = uartVTable;

s->port.baudRate = baudRate;

s->port.rxBufferSize = UART2_RX_BUFFER_SIZE;
s->port.txBufferSize = UART2_TX_BUFFER_SIZE;
s->port.rxBuffer = rx2Buffer;
s->port.txBuffer = tx2Buffer;

s->USARTx = USART2;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
// USART2_TX PA2
// USART2_RX PA3
gpio.speed = Speed_2MHz;
gpio.pin = Pin_2;
gpio.mode = Mode_AF_PP;
if (mode & MODE_TX)
gpioInit(GPIOA, &gpio);
gpio.pin = Pin_3;
gpio.mode = Mode_IPU;
if (mode & MODE_RX)
gpioInit(GPIOA, &gpio);

// RX/TX Interrupt
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

return s;
}

// USART3 - Telemetry (RX/TX by DMA + REMAP)
uartPort_t *serialUSART3(uint32_t baudRate, portMode_t mode)
{
uartPort_t *s;
static volatile uint8_t rx3Buffer[UART3_RX_BUFFER_SIZE];
static volatile uint8_t tx3Buffer[UART3_TX_BUFFER_SIZE];
gpio_config_t gpio;
NVIC_InitTypeDef NVIC_InitStructure;

s = &uartPort3;
s->port.vTable = uartVTable;

s->port.baudRate = baudRate;

s->port.rxBuffer = rx3Buffer;
s->port.txBuffer = tx3Buffer;
s->port.rxBufferSize = UART3_RX_BUFFER_SIZE;
s->port.txBufferSize = UART3_TX_BUFFER_SIZE;

// if we're only receiving, fall back to IRQ reception - workaround for spektrum sat polling
if (mode != MODE_RX)
s->rxDMAChannel = DMA1_Channel3;
s->txDMAChannel = DMA1_Channel2;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
// USART3_TX PB10
// USART3_RX PB11
gpio.speed = Speed_2MHz;
gpio.pin = Pin_10;
gpio.mode = Mode_AF_PP;
if (mode & MODE_TX)
gpioInit(GPIOB, &gpio);
gpio.pin = Pin_11;
gpio.mode = Mode_IPU;
if (mode & MODE_RX)
gpioInit(GPIOB, &gpio);

if (mode == MODE_RX) {
// RX Interrupt
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}

// DMA TX Interrupt
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

return s;
}

serialPort_t *uartOpen(USART_TypeDef *USARTx, serialReceiveCallbackPtr callback, uint32_t baudRate, portMode_t mode)
{
DMA_InitTypeDef DMA_InitStructure;
USART_InitTypeDef USART_InitStructure;

uartPort_t *s = NULL;

if (!USARTx)
return NULL;

if (USARTx == USART1)
s = serialUSART1(baudRate, mode);
if (USARTx == USART2)
s = serialUSART2(baudRate, mode);
if (USARTx == USART3)
s = serialUSART3(baudRate, mode);

s->USARTx = USARTx;

// common serial initialisation code should move to serialPort::init()
s->port.rxBufferHead = s->port.rxBufferTail = 0;
s->port.txBufferHead = s->port.txBufferTail = 0;
// callback for IRQ-based RX ONLY
s->port.callback = callback;
s->port.mode = mode;
s->port.baudRate = baudRate;

USART_InitStructure.USART_BaudRate = baudRate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
if (mode & MODE_SBUS) {
USART_InitStructure.USART_StopBits = USART_StopBits_2;
USART_InitStructure.USART_Parity = USART_Parity_Even;
} else {
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
}
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = 0;
if (mode & MODE_RX)
USART_InitStructure.USART_Mode |= USART_Mode_Rx;
if (mode & MODE_TX)
USART_InitStructure.USART_Mode |= USART_Mode_Tx;
USART_Init(USARTx, &USART_InitStructure);
USART_Cmd(USARTx, ENABLE);

DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USARTx->DR;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

// Receive DMA or IRQ
if (mode & MODE_RX) {
if (s->rxDMAChannel) {
DMA_InitStructure.DMA_BufferSize = s->port.rxBufferSize;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)s->port.rxBuffer;
DMA_DeInit(s->rxDMAChannel);
DMA_Init(s->rxDMAChannel, &DMA_InitStructure);
DMA_Cmd(s->rxDMAChannel, ENABLE);
USART_DMACmd(USARTx, USART_DMAReq_Rx, ENABLE);
s->rxDMAPos = DMA_GetCurrDataCounter(s->rxDMAChannel);
} else {
USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE);
}
}

// Transmit DMA or IRQ
if (mode & MODE_TX) {
if (s->txDMAChannel) {
DMA_InitStructure.DMA_BufferSize = s->port.txBufferSize;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_DeInit(s->txDMAChannel);
DMA_Init(s->txDMAChannel, &DMA_InitStructure);
DMA_ITConfig(s->txDMAChannel, DMA_IT_TC, ENABLE);
DMA_SetCurrDataCounter(s->txDMAChannel, 0);
s->txDMAChannel->CNDTR = 0;
USART_DMACmd(USARTx, USART_DMAReq_Tx, ENABLE);
} else {
USART_ITConfig(USARTx, USART_IT_TXE, ENABLE);
}
}

return (serialPort_t *)s;
}

void uartSetBaudRate(serialPort_t *instance, uint32_t baudRate)
{
USART_InitTypeDef USART_InitStructure;
uartPort_t *s = (uartPort_t *)instance;

USART_InitStructure.USART_BaudRate = baudRate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = 0;
if (s->port.mode & MODE_RX)
USART_InitStructure.USART_Mode |= USART_Mode_Rx;
if (s->port.mode & MODE_TX)
USART_InitStructure.USART_Mode |= USART_Mode_Tx;
USART_Init(s->USARTx, &USART_InitStructure);

s->port.baudRate = baudRate;
}

void uartSetMode(serialPort_t *s, portMode_t mode)
{
(void)s;
(void)mode;
// not implemented.
}


static void uartStartTxDMA(uartPort_t *s)
{
s->txDMAChannel->CMAR = (uint32_t)&s->port.txBuffer[s->port.txBufferTail];
if (s->port.txBufferHead > s->port.txBufferTail) {
s->txDMAChannel->CNDTR = s->port.txBufferHead - s->port.txBufferTail;
s->port.txBufferTail = s->port.txBufferHead;
} else {
s->txDMAChannel->CNDTR = s->port.txBufferSize - s->port.txBufferTail;
s->port.txBufferTail = 0;
}
s->txDMAEmpty = false;
DMA_Cmd(s->txDMAChannel, ENABLE);
}

uint8_t uartTotalBytesWaiting(serialPort_t *instance)
{
uartPort_t *s = (uartPort_t *)instance;
// FIXME always returns 1 or 0, not the amount of bytes waiting
if (s->rxDMAChannel)
return s->rxDMAChannel->CNDTR != s->rxDMAPos;
else
return s->port.rxBufferTail != s->port.rxBufferHead;
}

// BUGBUG TODO TODO FIXME - What is the bug?
bool isUartTransmitBufferEmpty(serialPort_t *instance)
{
uartPort_t *s = (uartPort_t *)instance;
if (s->txDMAChannel)
return s->txDMAEmpty;
else
return s->port.txBufferTail == s->port.txBufferHead;
}

uint8_t uartRead(serialPort_t *instance)
{
uint8_t ch;
uartPort_t *s = (uartPort_t *)instance;

if (s->rxDMAChannel) {
ch = s->port.rxBuffer[s->port.rxBufferSize - s->rxDMAPos];
if (--s->rxDMAPos == 0)
s->rxDMAPos = s->port.rxBufferSize;
} else {
ch = s->port.rxBuffer[s->port.rxBufferTail];
s->port.rxBufferTail = (s->port.rxBufferTail + 1) % s->port.rxBufferSize;
}

return ch;
}

void uartWrite(serialPort_t *instance, uint8_t ch)
{
uartPort_t *s = (uartPort_t *)instance;
s->port.txBuffer[s->port.txBufferHead] = ch;
s->port.txBufferHead = (s->port.txBufferHead + 1) % s->port.txBufferSize;

if (s->txDMAChannel) {
if (!(s->txDMAChannel->CCR & 1))
uartStartTxDMA(s);
} else {
USART_ITConfig(s->USARTx, USART_IT_TXE, ENABLE);
}
}

const struct serialPortVTable uartVTable[] = {
{
uartWrite,
uartTotalBytesWaiting,
uartRead,
uartSetBaudRate,
isUartTransmitBufferEmpty,
uartSetMode,
}
};

// Handlers

// USART1 Tx DMA Handler
void DMA1_Channel4_IRQHandler(void)
{
uartPort_t *s = &uartPort1;
DMA_ClearITPendingBit(DMA1_IT_TC4);
DMA_Cmd(s->txDMAChannel, DISABLE);

if (s->port.txBufferHead != s->port.txBufferTail)
uartStartTxDMA(s);
else
s->txDMAEmpty = true;
}

// USART3 Tx DMA Handler
void DMA1_Channel2_IRQHandler(void)
{
uartPort_t *s = &uartPort3;
DMA_ClearITPendingBit(DMA1_IT_TC2);
DMA_Cmd(s->txDMAChannel, DISABLE);

if (s->port.txBufferHead != s->port.txBufferTail)
uartStartTxDMA(s);
else
s->txDMAEmpty = true;
}

// USART1 Tx IRQ Handler
void USART1_IRQHandler(void)
{
uartPort_t *s = &uartPort1;
uint16_t SR = s->USARTx->SR;

if (SR & USART_FLAG_TXE) {
if (s->port.txBufferTail != s->port.txBufferHead) {
s->USARTx->DR = s->port.txBuffer[s->port.txBufferTail];
s->port.txBufferTail = (s->port.txBufferTail + 1) % s->port.txBufferSize;
} else {
USART_ITConfig(s->USARTx, USART_IT_TXE, DISABLE);
}
}
}

// USART2 Rx/Tx IRQ Handler
void USART2_IRQHandler(void)
{
uartPort_t *s = &uartPort2;
uint16_t SR = s->USARTx->SR;

if (SR & USART_FLAG_RXNE) {
// If we registered a callback, pass crap there
if (s->port.callback) {
s->port.callback(s->USARTx->DR);
} else {
s->port.rxBuffer[s->port.rxBufferHead] = s->USARTx->DR;
s->port.rxBufferHead = (s->port.rxBufferHead + 1) % s->port.rxBufferSize;
}
}
if (SR & USART_FLAG_TXE) {
if (s->port.txBufferTail != s->port.txBufferHead) {
s->USARTx->DR = s->port.txBuffer[s->port.txBufferTail];
s->port.txBufferTail = (s->port.txBufferTail + 1) % s->port.txBufferSize;
} else {
USART_ITConfig(s->USARTx, USART_IT_TXE, DISABLE);
}
}
}

// USART3 Rx IRQ Handler
void USART3_IRQHandler(void)
{
uartPort_t *s = &uartPort3;
uint16_t SR = s->USARTx->SR;

if (SR & USART_FLAG_RXNE) {
// If we registered a callback, pass crap there
if (s->port.callback) {
s->port.callback(s->USARTx->DR);
} else {
s->port.rxBuffer[s->port.rxBufferHead] = s->USARTx->DR;
s->port.rxBufferHead = (s->port.rxBufferHead + 1) % s->port.rxBufferSize;
}
}
}
Qic
Поменял битовое на логическое, спасибо, забыл как это делается. Про конструкции выражений тоже учту.
Вот только все также в терминале наблюдается следующее:
Нажмите для просмотра прикрепленного файла
Также идут проглатывания на отправке, и перестаёт работать видимо при использовании vTaskDelay, видимо особенности работы с FreeRTOS 6.4.0.
Но в первую очередь интересно почему проглатывается.

sKWO, Я посмотрел драйвер, но FIFO это кажется не совсем то что мне нужно. Насколько Я понимаю происходит постоянный сбор в буфер всего приемного и обработчик уже должен там самостоятельно выдёргивая по байту разбираться что это и куда. Я же хочу чтобы если что-то появлялось на входе в USART, то оно как правило будет иметь тишину, в частности позже хочу прикрутить туда modbus. Впринципе любые данные будут когда либо иметь конец и паузу, которую Я и ловлю, после чего всё что пришло анализирую.
Насколько Я понимаю можно спокойно сделать и FIFO, не понадобится перезапускать приемный DMA и по IDLE ловить конец. Но хочется разобраться где у меня косяки в DMA что при передачи глотает байты.

P.S. IgorKossak спасибо за форматирование.
sKWO
Цитата(Qic @ Jul 21 2016, 12:10) *
Насколько Я понимаю можно спокойно сделать и FIFO, не понадобится перезапускать приемный DMA и по IDLE ловить конец. Но хочется разобраться где у меня косяки в DMA что при передачи глотает байты.

Я дма использую только для передачи. Для приёма лучше ложить в фифо а дальше с очередями работать и не мучиться с дма. ДМА удобно по приёму например с АЦП но не для приема данных с серийного интерфейса, ИМХО.
Вот повтор кода который я выкладывал:
Код
// USART3 Rx IRQ Handler
void USART3_IRQHandler(void)
{
    uartPort_t *s = &uartPort3;
    uint16_t SR = s->USARTx->SR;

   [b][/b] if (SR & USART_FLAG_RXNE) {[/b]
        // If we registered a callback, pass crap there
        if (s->port.callback) {
            s->port.callback(s->USARTx->DR);
        } else {
            s->port.rxBuffer[s->port.rxBufferHead] = s->USARTx->DR;
            s->port.rxBufferHead = (s->port.rxBufferHead + 1) % s->port.rxBufferSize;
        }
    }
}

сначала проверили или по приему сюда попали а дальше или каллбэк на функцию или байт в фифо.
так удобнее.
Qic
Т.е. логика работы выглядит так?:
DMA постоянно гонит данные в буфер, без остановки, когда у нас есть свободное время, мы начинаем анализировать этот самый FIFO буфер на совпадение.
Звучит быстро и просто, проверяем 64 байта на совпадение и обрабатываем.
А что будет если байт 200, или больше? Что если большой, как говорится, payload? А если система комманд исчисляется десятками? Проверять несколько раз буфер на все соответствия?
Мне кажется это расточительно по ресурсам, хочется ловить из потока нужные данные, и тратить на это минимум времени просто проверяя заголовок сразу в известном месте - в начале буфера.
Чисто логически FIFO правильное решение, но это очень много времени на проверку всего потока.
amiller
Цитата(Qic @ Jul 21 2016, 22:28) *
Т.е. логика работы выглядит так?:
DMA постоянно гонит данные в буфер, без остановки, когда у нас есть свободное время, мы начинаем анализировать этот самый FIFO буфер на совпадение.
Звучит быстро и просто, проверяем 64 байта на совпадение и обрабатываем.
А что будет если байт 200, или больше? Что если большой, как говорится, payload? А если система комманд исчисляется десятками? Проверять несколько раз буфер на все соответствия?
Мне кажется это расточительно по ресурсам, хочется ловить из потока нужные данные, и тратить на это минимум времени просто проверяя заголовок сразу в известном месте - в начале буфера.
Чисто логически FIFO правильное решение, но это очень много времени на проверку всего потока.

Не совсем понимаю, в чём Вы видите проблему.
1. Есть кольцевой буфер.
2. Есть процесс "писатель" - DMA с своим указателем на память.
3. Есть процесс "читатель" - функция анализа потока, со своим указателем.
4. В каждом сеансе чтения читаются только те принятые байты, которые были приняты с момента предыдущего сеанса.
5. Размер буфера выбирается достаточным для конкретных условий.
6. Процесс "читатель" обычно ищет в потоке заголовок пакета. Затем по заголовку определяет длину пакета и ждёт, пока будет принято нужное число байт. После получения полного пакета - анализ и обработка.
7. Если используется протокол на базе временных интервалов, например ModBus RTU, то по фиксации паузы в потоке можно инициализировать указатели на кольцевой буфер.
И всё, ничего сложного, и нет лишних затрат времени CPU.
x893
Цитата(sKWO @ Jul 21 2016, 14:55) *
.


Может так и задумано, но есть нюансы.
1. Не обрабатываются ошибки
2. Новые данные затирают старые
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.