Создание собственной системы стабилизации

rual

Это базефлайт? Насчет начальной установки счетчика можно не париться, после сброса он в нуле, для спокойствия его надо было через надстроечную структуру прописать DMA_InitStruct.DMA_BufferSize = 0;.

SergDoc

Ну вот, вроде всё работает без костыля github.com/SergDoc/…/drv_uart.c затронут ещё main.c , так что требуется перекомпиляция проекта, надеюсь 100 микросекунд никого не убъёт?

#define UART1_TX_PIN        GPIO_Pin_9
#define UART1_RX_PIN        GPIO_Pin_10
#define UART1_GPIO          GPIOA
#define UART1_TX_PINSOURCE  GPIO_PinSource9
#define UART1_RX_PINSOURCE  GPIO_PinSource10


#define UART1_BUFFER_SIZE   2048


// Receive buffer, circular DMA
volatile uint8_t rxBuffer[UART1_BUFFER_SIZE];
uint32_t rxDMAPos = 0;


volatile uint8_t txBuffer[UART1_BUFFER_SIZE];
uint16_t txBufferTail = 0;
uint16_t txBufferHead = 0;


///////////////////////////////////////////////////////////////////////////////
// UART1 Transmit via DMA
///////////////////////////////////////////////////////////////////////////////


static void uartTxDMA(void)
{
    DMA2_Stream7->M0AR = (uint32_t)&txBuffer[txBufferTail];
    if (txBufferHead > txBufferTail)
    {
            DMA_SetCurrDataCounter(DMA2_Stream7, txBufferHead - txBufferTail);
            txBufferTail = txBufferHead;
    }
    else
    {
            DMA_SetCurrDataCounter(DMA2_Stream7, UART1_BUFFER_SIZE - txBufferTail);
            txBufferTail = 0;
    }


    DMA_Cmd(DMA2_Stream7, ENABLE);
}


///////////////////////////////////////////////////////////////////////////////
// UART1 TX Complete Interrupt Handler
///////////////////////////////////////////////////////////////////////////////


void DMA2_Stream7_IRQHandler(void)
{
    DMA_ClearITPendingBit(DMA2_Stream7, DMA_IT_TCIF7);
    DMA_Cmd(DMA2_Stream7, DISABLE);


    if (txBufferHead != txBufferTail)
            uartTxDMA();
}


///////////////////////////////////////////////////////////////////////////////
// Telemetry Initialization
///////////////////////////////////////////////////////////////////////////////


void uartInit(uint32_t speed)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    DMA_InitTypeDef   DMA_InitStructure;
    NVIC_InitTypeDef  NVIC_InitStructure;


    GPIO_StructInit(&GPIO_InitStructure);
    USART_StructInit(&USART_InitStructure);
    DMA_StructInit(&DMA_InitStructure);


    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,  ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,   ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);


    GPIO_InitStructure.GPIO_Pin   = UART1_TX_PIN | UART1_RX_PIN;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    //GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;


    GPIO_PinAFConfig(UART1_GPIO, UART1_TX_PINSOURCE, GPIO_AF_USART1);
    GPIO_PinAFConfig(UART1_GPIO, UART1_RX_PINSOURCE, GPIO_AF_USART1);


    GPIO_Init(UART1_GPIO, &GPIO_InitStructure);


    // DMA TX Interrupt
    NVIC_InitStructure.NVIC_IRQChannel                   = DMA2_Stream7_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;


    NVIC_Init(&NVIC_InitStructure);


    USART_InitStructure.USART_BaudRate            = speed;
    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(USART1, &USART_InitStructure);


    // Receive DMA into a circular buffer


    DMA_DeInit(DMA2_Stream5);


    DMA_InitStructure.DMA_Channel            = DMA_Channel_4;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
    DMA_InitStructure.DMA_Memory0BaseAddr    = (uint32_t)rxBuffer;
    //DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralToMemory;
    DMA_InitStructure.DMA_BufferSize         = UART1_BUFFER_SIZE;
    //DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    //DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    //DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority           = DMA_Priority_Medium;
    //DMA_InitStructure.DMA_FIFOMode           = DMA_FIFOMode_Disable;
    //DMA_InitStructure.DMA_FIFOThreshold      = DMA_FIFOThreshold_1QuarterFull;
    //DMA_InitStructure.DMA_MemoryBurst        = DMA_MemoryBurst_Single;
    //DMA_InitStructure.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single;


    DMA_Init(DMA2_Stream5, &DMA_InitStructure);


    DMA_Cmd(DMA2_Stream5, ENABLE);


    USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);


    rxDMAPos = DMA_GetCurrDataCounter(DMA2_Stream5);


    // Transmit DMA
    DMA_DeInit(DMA2_Stream7);


    //DMA_InitStructure.DMA_Channel            = DMA_Channel_4;
    //DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
    DMA_InitStructure.DMA_Memory0BaseAddr    = (uint32_t)txBuffer;
    DMA_InitStructure.DMA_DIR                = DMA_DIR_MemoryToPeripheral;
    //DMA_InitStructure.DMA_BufferSize         = UART_BUFFER_SIZE;
    //DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    //DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    //DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    //DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Normal;
    //DMA_InitStructure.DMA_Priority           = DMA_Priority_Medium;
    //DMA_InitStructure.DMA_FIFOMode           = DMA_FIFOMode_Disable;
    //DMA_InitStructure.DMA_FIFOThreshold      = DMA_FIFOThreshold_1QuarterFull;
    //DMA_InitStructure.DMA_MemoryBurst        = DMA_MemoryBurst_Single;
    //DMA_InitStructure.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single;


    DMA_Init(DMA2_Stream7, &DMA_InitStructure);


    DMA_SetCurrDataCounter(DMA2_Stream7, 0);


    DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);


    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);


    USART_Cmd(USART1, ENABLE);
}

bool isUartAvailable(void)
{
    return (DMA_GetCurrDataCounter(DMA2_Stream5) != rxDMAPos) ? true : false;
}
//bool isUartTransmitDMAEmpty(void)
//		{
//		    return txDMAEmpty;
//		}

//bool isUartTransmitEmpty(void)
//{
//    return (txBufferTail == txBufferHead);
//}

uint8_t uartRead(void)
{
    uint8_t ch;

    ch = rxBuffer[UART1_BUFFER_SIZE - rxDMAPos];
    // go back around the buffer
    if (--rxDMAPos == 0)
        rxDMAPos = UART1_BUFFER_SIZE;

    return ch;
}

uint8_t uartReadPoll(void)
{
    while (!isUartAvailable()); // wait for some bytes
    return uartRead();
}

void uartWrite( uint8_t ch)
{
	 // while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE) {
		//	};
    txBuffer[txBufferHead] = ch;

    txBufferHead = (txBufferHead + 1) % UART1_BUFFER_SIZE;


    // if DMA wasn't enabled, fire it up
    if (!(DMA2_Stream7->CR & 1))
        uartTxDMA();
		delayMicroseconds(100);
}

void uartPrint(char *str)
{
    while (*str)
        uartWrite(*(str++));
}
SergDoc

Кстати а вот и ошибка, почему нифига не работало:

DMA_InitStructure.DMA_Memory0BaseAddr    = (uint32_t)txBuffer;

а небыло этой строки вообще 😃

rual
SergDoc:

а небыло этой строки вообще

Этож строка только для Ф4. Как она вообще со старой компилилась,? Либо у тебя альтернативная структура для Ф1 гдето в заголовках болтается.

SergDoc

Да нет вот от Ф1 нету ничего, этот проект я полностью пересобирал и цеплял всё сам, компилилась зараза 😦
Не буду утверждать, но когда я делал этот драйвер, то нигде не мог найти подходящей инфы, а тут смотрю аероквады (не путать с автоквад) ковыряют так до боли знакомый кодятник (с тойже бетой) - всё никаких git - надоело 😦

SergDoc

Поковырялся в кодятнике code.google.com/p/aq32plus/ - вот это блин солянка, и вии и baseflight и px4 и даже рево (я учусь makefile делать далеко не удачный пример 😦 )

mataor
Sir_Alex:

которые в теории умеют CAN

хех… почему в теории? походу просто ребята сварганили клона создав свою разводку, а прошивка родная от ESC32

а цена действительно шикарная… жалко я сейчас некредитоспособен…

Sir_Alex
mataor:

хех… почему в теории?

Ну потому что ESC32 в теории CAN умеет, а по факту, реализации нету в коде (ну или не было раньше, мот уже есть)

SergDoc

главное из этого - чтобы лапы can не были заняты шимом выходным, или ещё чем…

rual
Sir_Alex:

Ну потому что ESC32 в теории CAN умеет, а по факту, реализации нету в коде (ну или не было раньше, мот уже есть)

Если в коде нет, то нафиг они за такую цену. Если только как плата под разработку ПО, но всё равно дорого.

Sir_Alex
rual:

Если в коде нет, то нафиг они за такую цену. Если только как плата под разработку ПО, но всё равно дорого.

Если в коде нет, значит можно дописать 😃
А если ждать когда кто нибудь всё придумает и сделает, то зачем эта тема нужна?
Да и цена вроде адекватная, по сравнению с оригиналом (ESC32)

RaJa
Sir_Alex:

Если в коде нет, значит можно дописать 😃
А если ждать когда кто нибудь всё придумает и сделает, то зачем эта тема нужна?
Да и цена вроде адекватная, по сравнению с оригиналом (ESC32)

Насколько я помню - они до 4S только, а значит применение очень ограниченное - в основном мелкие коптеры-игрушки и FPV. То есть бестолковые. Там, где нужна обратная связь и раскрываются преимущества CAN, требуется и напряжение побольше, для серьезных машинок.

rual
Sir_Alex:

Если в коде нет, значит можно дописать

Это понятно, но кто будет писать? У меня 6 месяце ушло на понимание принципов получения горизонта от ДУСа и акселя, хотя вроде всё понятно и примеров море. А чтоб нормальный код регуля написать нужно подробно изучить принципы и теорию работы БК мотора как электромашины (векторы напряжения и тока, скольжение поля и пр., я давно это мимо проходил), описать это коде. Тупой порт открытого (и правильного (!)) кода, а надо ещё найти таковой, может дать только обратку по оборотам, ну и возможно, если схема предусматривает, токам фаз.

RaJa:

где нужна обратная связь и раскрываются преимущества CAN, требуется и напряжение побольше, для серьезных машинок.

Можно поподробней, какие параметры гонят регули обратно и как их использовать? Пока представляю только 2 применения: диагностика аварийных ситуаций и автоподстройка ПИДов.

RaJa
rual:

Это понятно, но кто будет писать? У меня 6 месяце ушло на понимание принципов получения горизонта от ДУСа и акселя, хотя вроде всё на понятно и примеров море. А чтоб нормальный код регуля написать нужно подробно изучить принципы и теорию работы БК мотора как электромашины (векторы напряжения и тока, скольжение поля и пр., я давно это мимо проходил), описать это коде. Тупой порт открытого (и правильного (!)) кода, а надо ещё найти таковой, может дать только обратку по оборотам, ну и возможно, если схема предусматривает, токам фаз.

Можно поподробней, какие параметры гонят регули обратно и как их использовать? Пока представляю только 2 применения: диагностика аварийных ситуаций и автоподстройка ПИДов.

Какие именно эти регули гонят - не знаю, но принципиально могут возвращать информацию о срыве синхронизации, вращается мотор или нет, какой ток потребляет каждый мотор, с какой частотой вращается мотор и на основании этих данных можно более точно управлять, ловить аварийные ситуации и садиться с частичной потерей двигателей, отключать мотор, застрявший в траве при приземлении и много чего еще. Но это нетривиальная автоматика. Тут требуется серьезная поддержка в полетном контроллере. Я таких полетных контроллеров с открытым кодом не знаю.

Sir_Alex
rual:

А чтоб нормальный код регуля написать нужно подробно изучить принципы и теорию работы БК мотора как электромашины (векторы напряжения и тока, скольжение поля и пр., я давно это мимо проходил), описать это коде. Тупой порт открытого (и правильного (!)) кода, а надо ещё найти таковой, может дать только обратку по оборотам, ну и возможно, если схема предусматривает, токам фаз.

Так софт под этот регуль Open Source, и он доступен, я имел ввиду только добавить поддержку CAN.

rual:

Можно поподробней, какие параметры гонят регули обратно и как их использовать? Пока представляю только 2 применения: диагностика аварийных ситуаций и автоподстройка ПИДов.

Так пока нету таких систем, которые бы использовали подобную информацию, вот и ниодин геруль их и не гонит (да и через PWM это невозможно).
На сколько я помню, vis.asta например получал от каждого регуля потребляемый ток, температуру (двигателя? регуля?), обороты. Ну и наверное нештатные ситуации, типа заклинивания.
Даже если контроллер и не будет в состоянии учитывать эту информацию, то ее можно хотя бы в телеметрию пускать (ну и в лог писать). Возможно это поможет в случае краша, разобраться что не так было.

RaJa
Sir_Alex:

Так пока нету таких систем, которые бы использовали подобную информацию, вот и ниодин геруль их и не гонит (да и через PWM это невозможно).
На сколько я помню, vis.asta например получал от каждого регуля потребляемый ток, температуру (двигателя? регуля?), обороты. Ну и наверное нештатные ситуации, типа заклинивания.
Даже если контроллер и не будет в состоянии учитывать эту информацию, то ее можно хотя бы в телеметрию пускать (ну и в лог писать). Возможно это поможет в случае краша, разобраться что не так было.

Да, Виктор получает довольно много инфы с регулей, они у него как раз на CAN шине. Для телеметрии инфы слишком много - частота работы регуля обычно от 8 кГц. А если писать только события, то ее все равно надо обрабатывать на борту. В любом случае, я не вижу смысла ставить такие регули пока нет готовности с ними работать по полной, иначе слишком дорого, а толку чуть.

oleg70

Кто нибудь подскажите: как ошибку USART посчитать (%-baudrate) у F303 ??
Начальные условия: кварц-16 Мгц, требуемая частота fCLK - 56 мгц… В даташите таблица есть только под 8-мгц кварц…
Чет похоже у меня проблема с обменом между “камнями” из за этого… (часто зависает).

mataor

хмм… насколько помню она считается практически идентично для всех процессоров и является по сути отклонением расчетного значения счетчиков от задаваемого

SergDoc
oleg70:

В даташите таблица есть только под 8-мгц кварц…

А причём тут кварц? всё равно она делится до 1МГц-а какой бы кварц не стоял, и все расчёты делителей скачут уже от этого…

oleg70
SergDoc:

А причём тут кварц?

Example 1
To obtain 9600 baud with fCK = 8 MHz.
● In case of oversampling by 16:
USARTDIV = 8 000 000/9600
BRR[31:0] = USARTDIV = 833d = 0x0341
● In case of oversampling by 8:
USARTDIV = 2 * 8 000 000/9600
USARTDIV = 1666,66 (1667d = 0x683)
BRR[3:0] = 0x3 << 1 = 0x1
BRR = 0x681
Tx/Rx baud
fCK
USARTDIV
-------------------------------= -
Tx/Rx baud
2×fCK
USARTDIV
= --------------------------------
Tx/Rx baud
fCK
USARTDIV
= --------------------------------

Тут еще “оверсамплинг” какой то, короче я запутался…

Error calculation for programmed baud rates at fCK = 72 MHz
for oversampling by 16 and by 8
Baudrate Oversampling by 16 (OVER8 = 0) Oversampling by 8 (OVER8 = 1)
S.No Desired Actual
USART_
BRR value
% Error =
(calculated -
Desired
baudrate)/Desired