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

mataor

хм… если в программе время цикла неизменно а в реальности меняется - значит у вас приоритет системного времени низкий + на что-то уходит сильно много времени в обработчике уарта (хотя на что там времени тратится - по готовности засунуть байт в уарт и спать до следующего).
ради интереса сегодня-завтра посмотрю что у меня творится

SergDoc

Я тоже думаю что причина не в УАРТЕ, и время цикла правильное что приходит на ум 22милисекунды - какой-то баг по гире? системное время в высшем приоритете!

да и первый уарт работает на прямую с памятью, не отвлекая жирный проц 😃 - как бы не разленился 😃

SergDoc

ну да как-то дороговато 😦 хотя если посмотреть иму платы от тех же виртуалроботикс (мультипилот) то цена приемлимая…

Sir_Alex

Плата дорогая, 5983 и 5883 практически не отличаются по цене, не понятно с чего бы этой платке стоить дороже того же CRIUS’a (или других контроллеров, коих сейчас полно)

З.Ы. 5983 можно прикупть на eBay баксов за 7
З.Ы.Ы. VRBrain такая же дорогая поделка как и PX4 - не вижу смысла переплачивать за такие девайсы.

SergDoc
Sir_Alex:

З.Ы.Ы. VRBrain такая же дорогая поделка как и PX4 - не вижу смысла переплачивать за такие девайсы.

ну как бэ плата то есть конкурентная и явно дешевле 😃 а вот 5983 можно повесить на свободный spi (это в текущей версии), а 5883 можно и отключать программно или заменить вроде подключение одинаковое если по i2c…
и похоже развод это, зачем 5983 цеплять по i2c да пусть к той же mpu?

okan_vitaliy
SergDoc:

Я тоже думаю что причина не в УАРТЕ, и время цикла правильное что приходит на ум 22милисекунды - какой-то баг по гире? системное время в высшем приоритете!

да и первый уарт работает на прямую с памятью, не отвлекая жирный проц 😃 - как бы не разленился 😃

Вот тут делаю измерение-
LED3_ON;
serialCom();
LED3_OFF;
Измеряю время работы функции обмена. Меряем на светодиоде. Когда происходит чтение реальных данных из гуи, то имеем время 19мс. А обработка обмена у нас висит как раз в основном цикле. Частота этих багов соответствует частоте опроса из гуи. Интересно что б кто нибудь проверил. Проверять только в режиме чтения реальных данных в вингуи или в мультивий-конфигураторе.

SergDoc

this code is executed at each loop and won’t interfere with control loop if it lasts less than 650 microseconds

т.е. если бы оно тормозило цикл это было бы видно в гуи, и уж у Таймкопа аппараты точно падали при подключении к гуи, да и вий бы никуда не полетел, хотя если мы допустим из 400, расчётов потеряем даже 60% -70% ничего не случится, скоро предоставится возможность всё перепроверить - проверю обязательно… сейчас тупо лень с аппарата плату снимать 😃

okan_vitaliy
SergDoc:

т.е. если бы оно тормозило цикл это было бы видно в гуи, и уж у Таймкопа аппараты точно падали при подключении к гуи, да и вий бы никуда не полетел, хотя если мы допустим из 400, расчётов потеряем даже 60% -70% ничего не случится, скоро предоставится возможность всё перепроверить - проверю обязательно… сейчас тупо лень с аппарата плату снимать 😃

Таймкоп не причем помойму.
Вот тут проблемка
void uartWrite( uint8_t ch)
{
LED3_ON;
while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE) {
};
LED3_OFF;
txBuffer[txBufferHead] = ch;

Цикл ожидания равен примерно 87 мкс. И это на каждом байте. Можете прикинуть тормоза системы при усиленном обмене. Тут чето с дма не то.

mataor

хех… ну как я и говорил - проблемка в уарте, а не в функции serialcom

SergDoc

если его убрать УАРТ вешается, или отказываться от ДМА или добивать этот чёртов уарт, по всей видимости переполняется буфер ДМА ибо пихаем слишком быстро в него, я как и писал выше воткнул этот костыль, ибо не нашел ошибку, при этом уарт на приём работает, а на выдачу затыкается,
первая версия была такая:

// Receive buffer, circular DMA
volatile uint8_t rxBuffer[UART_BUFFER_SIZE];
uint32_t rxDMAPos = 0;
volatile uint8_t txBuffer[UART_BUFFER_SIZE];
uint32_t txBufferTail = 0;
uint32_t txBufferHead = 0;


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


    DMA_Cmd(DMA2_Stream7, ENABLE);
}


void DMA2_Stream7_IRQHandler(void)
{


    if(DMA_GetITStatus(DMA2_Stream7, DMA_IT_TCIF7))
           {
                        DMA_ClearITPendingBit(DMA2_Stream7, DMA_IT_TCIF7);


                        DMA_Cmd(DMA2_Stream7, DISABLE);
                     }


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


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



        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
        // USART1_TX    PA9
    // USART1_RX    PA10
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
      GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);


    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);








    // DMA TX Interrupt
        /* Configure the Priority Group to 2 bits */
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);




        RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1 , ENABLE);
    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_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);




        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
    // Receive DMA into a circular buffer
    DMA_DeInit(DMA2_Stream5);//
    DMA_InitStructure.DMA_Channel = DMA_Channel_4;//
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//
    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_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;//
    DMA_InitStructure.DMA_BufferSize = UART_BUFFER_SIZE;//
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//
    DMA_Init(DMA2_Stream5, &DMA_InitStructure);//
    DMA_Cmd(DMA2_Stream5, ENABLE);//
    /* Enable the USART Rx DMA request */
        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_DIR = DMA_DIR_MemoryToPeripheral;
    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;
        DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
      DMA_InitStructure.DMA_FIFOMode =DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;








        DMA_Init(DMA2_Stream7, &DMA_InitStructure);





        DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);


        DMA_SetCurrDataCounter(DMA2_Stream7, 0);//DMA2_Stream7->NDTR = 0;


        USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);


        USART_Cmd(USART1, ENABLE);
}


uint16_t uartAvailable(void)
{
    return (DMA_GetCurrDataCounter(DMA2_Stream5) != rxDMAPos) ? true : false;
}


bool uartTransmitEmpty(void)
{
    return (txBufferTail == txBufferHead);
}


uint8_t uartRead(void)
{
    uint8_t ch;


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


    return ch;
}


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


void uartWrite(uint8_t ch)
{
    txBuffer[txBufferHead] = ch;
    txBufferHead = (txBufferHead + 1) % UART_BUFFER_SIZE;


    // if DMA wasn't enabled, fire it up
    if (DMA_GetCmdStatus(DMA2_Stream7) == DISABLE)//(!(DMA2_Stream7->CR & 1))


        uartTxDMA();
}


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

она вешалась:(

может сделать так:

void uartWrite(uint8_t ch)
// if DMA wasn't enabled, fire it up
{
if (!(DMA2_Stream7->CR & 1)) {
txBuffer[txBufferHead] = ch;

txBufferHead = (txBufferHead + 1) % UART_BUFFER_SIZE;

uartTxDMA();
}
}
rual
okan_vitaliy:

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

Цикл ожидания равен примерно 87 мкс. И это на каждом байте. Можете прикинуть тормоза системы при усиленном обмене. Тут чето с дма не то.

Верно, это косяк! Смысл использования ПДП как раз в том чтобы ничего не ждать! uartWrite должна вычислять хвост буфера ПДП и класть символ в [ХВОСТ+1], и увеличивать счетчик ПДП на один. Если ХВОСТ+1 упирается в конец буфера, то возвращать признак переполнения, по нему пропускать вывод в порт, либо ждать завершения с циклом счёта. А ещё лучше сделать кольцевой буфер и при записи писать по кругу, и только если догнал хвост возвращать переполнение. Важно перед операциями с регистрами ПДП запрещать запрос канала.

mataor

кстати такой вопрос…
а в чем глобальный смысл использовать ДМА для уарта?
разницу в использовании процессора наврятли заметим - уарт работает без участия ЦПУ, а по времени засунуть байт в уарт или в ДМА -> уарт думаю что разницы не будет.

единственная разница будет в том случае, если ДМА будем передавать весь буфер целиком.

так что без переработки этого участка плюсов в ДМА не вижу.

rual
mataor:

а в чем глобальный смысл использовать ДМА для уарта?

правильный вопрос! объем данных и частота передачи не высокие. Кольцевой буфер на прерываниях никак не замедлит проц.

okan_vitaliy:

Короче без кольцевого тут не обойтись.

это оптимальное решение.

SergDoc

легко проверить - перенаправить всё во 2-й уарт (он без ДМА) если всё канает то и убирать ДМА к чертям, получается Ф4 пихает в UART столько, что тот не может пережевать, и увеличение буфера да и кольцевой тут не помогут - всё равно рано или позно наступит переполнение.
во всех файлах где есть uart1 заменить на 2, остальное вроде идентично - третий по умолчанию gps…
а если при переполнении буфера его тупо очищать - потеря нескольких пакетов ну как-то вообще ничего не испортит?
у меня сейчас есть некоторые затруднения - сам проверить ничего не могу 😦

Sir_Alex

ИМХО, в данной ситуации, надо дропать пакеты. Протокол ессесно должен уметь это переваривать (ну т.е. ничего страшного не должно произойти если что то не дойдет до адресата).

SergDoc

будет писать в счётчик ошибок пакетов в гуи - только и всего…

вот это работает, только не знаю с ДМА или без, просто прошивку заливал себе проверить порт: code.google.com/p/uavp-mods/

void InitSerialPort(uint8 s) {
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	DMA_InitTypeDef DMA_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	SerialPortDef * u;

	u = &SerialPorts[s];

	if (u->Used) {

		GPIO_StructInit(&GPIO_InitStructure);
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
#ifdef STM32F1
		GPIO_InitStructure.GPIO_Pin = u->TxPin;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
		GPIO_Init(u->Port, &GPIO_InitStructure);

		GPIO_InitStructure.GPIO_Pin = u->RxPin;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
		GPIO_Init(u->Port, &GPIO_InitStructure);
#else
		GPIO_StructInit(&GPIO_InitStructure);
		GPIO_InitStructure.GPIO_Pin = u->TxPin | u->RxPin;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
		//GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
		GPIO_Init(u->Port, &GPIO_InitStructure);

		GPIO_PinAFConfig(u->Port, u->TxPinSource, u->USART_AF);
		GPIO_PinAFConfig(u->Port, u->RxPinSource, u->USART_AF);
#endif
		USART_StructInit(&USART_InitStructure);
		//USART_InitStruct->USART_Parity = USART_Parity_No;
		USART_InitStructure.USART_BaudRate = u->Baud;
		USART_Init(u->USART, &USART_InitStructure);

		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

		TxQTail[s] = TxQHead[s] = TxQNewHead[s] = 0;

		if (u->DMAUsed) {
			// Common
			DMA_StructInit(&DMA_InitStructure);
			DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
			DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
			DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32) &u->USART->DR;
			DMA_InitStructure.DMA_BufferSize = SERIAL_BUFFER_SIZE;

			// Receive DMA
			DMA_DeInit(u->RxDMAStream);

			DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32) &u->USART->DR;
			DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
#ifdef STM32F1
			DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
			DMA_InitStructure.DMA_MemoryBaseAddr = (uint32) RxQ[s];
#else
			while (DMA_GetCmdStatus(u->RxDMAStream) != DISABLE) {
			};
			DMA_InitStructure.DMA_Channel = u->DMAChannel;
			DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
			DMA_InitStructure.DMA_Memory0BaseAddr = (uint32) RxQ[s];
#endif
			DMA_Init(u->RxDMAStream, &DMA_InitStructure);
			DMA_Cmd(u->RxDMAStream, ENABLE);
			USART_DMACmd(u->USART, USART_DMAReq_Rx, ENABLE);

			RxQTail[s] = RxQHead[s] = SERIAL_BUFFER_SIZE
					- DMA_GetCurrDataCounter(u->RxDMAStream);

			// Transmit DMA
			NVIC_InitStructure.NVIC_IRQChannel = u->TxDMAISR;
			NVIC_Init(&NVIC_InitStructure);

			DMA_DeInit(u->TxDMAStream);
#ifdef STM32F1
			DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
#else
			while (DMA_GetCmdStatus(u->TxDMAStream) != DISABLE) {
			};
			DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
			//	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32) TxQ[s];
#endif
			DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
			DMA_Init(u->TxDMAStream, &DMA_InitStructure);

			DMA_SetCurrDataCounter(u->TxDMAStream, 0);
			DMA_ITConfig(u->TxDMAStream, DMA_IT_TC, ENABLE);

			USART_DMACmd(u->USART, USART_DMAReq_Tx, ENABLE);
		} else if (u->InterruptsUsed) {
			RxQTail[s] = RxQHead[s] = 0;
			NVIC_InitStructure.NVIC_IRQChannel = u->ISR;
			NVIC_Init(&NVIC_InitStructure);

			USART_ITConfig(u->USART, USART_IT_RXNE, ENABLE);
		}
		USART_Cmd(u->USART, ENABLE);
	}

} // InitSerialPort
oleg70
okan_vitaliy:

Короче мне так кажется - нужно loop садить куда нибудь в прерывание.

Да, алгоритм стабилизации в первую очередь…, и других прерываний как можно меньше стараться делать тогда время цикла стабильно.
Тем более все успевает обработаться включая ПИД даже на моих 72Мгц (не говоря уж 168)…

rual
oleg70:

все успевает обработаться включая ПИД даже на моих 72Мгц (не говоря уж 168)…

У меня на Ф1 без аппаратной плавучки всё успевало работать в прерываниях.

Coreglider

Приём по юсарту я использую в прерываниях, передачу - через DMA.

dmaInitStr.DMA_BufferSize=4; //неважно
dmaInitStr.DMA_DIR=DMA_DIR_PeripheralDST;
dmaInitStr.DMA_M2M=DMA_M2M_Disable;
dmaInitStr.DMA_MemoryBaseAddr=(u32)commTxBuffer;
dmaInitStr.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
dmaInitStr.DMA_MemoryInc=DMA_MemoryInc_Enable;
dmaInitStr.DMA_Mode=DMA_Mode_Normal;
dmaInitStr.DMA_PeripheralBaseAddr=(uint32_t) &(COMM_USART->DR);
dmaInitStr.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
dmaInitStr.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
dmaInitStr.DMA_Priority=DMA_Priority_Medium;
DMA_Init(COMM_DMA_CHANNEL,&dmaInitStr);
void sendCommInterface(unsigned short num)
{
if(DMA_GetCurrDataCounter(COMM_DMA_CHANNEL) ==0){
DMA_SetCurrDataCounter(COMM_DMA_CHANNEL,num);
DMA_Cmd(COMM_DMA_CHANNEL,ENABLE); }
}

Но, после отправки посылки через дма надо проверять флаг USART_SR_TC
чтобы удостовериться что данные точно ушли, ибо конец передачи дма не означает конец передачи данных, и запись новых данных приводит к потерям.