Создание собственной системы стабилизации
хм… если в программе время цикла неизменно а в реальности меняется - значит у вас приоритет системного времени низкий + на что-то уходит сильно много времени в обработчике уарта (хотя на что там времени тратится - по готовности засунуть байт в уарт и спать до следующего).
ради интереса сегодня-завтра посмотрю что у меня творится
Я тоже думаю что причина не в УАРТЕ, и время цикла правильное что приходит на ум 22милисекунды - какой-то баг по гире? системное время в высшем приоритете!
да и первый уарт работает на прямую с памятью, не отвлекая жирный проц 😃 - как бы не разленился 😃
Ловите вкусняшку - 10DOF платка и никакого и2с - все по SPI можно подключить www.aliexpress.com/item/…/925324231.html
хотя дороговато немного)))
ну да как-то дороговато 😦 хотя если посмотреть иму платы от тех же виртуалроботикс (мультипилот) то цена приемлимая…
Плата дорогая, 5983 и 5883 практически не отличаются по цене, не понятно с чего бы этой платке стоить дороже того же CRIUS’a (или других контроллеров, коих сейчас полно)
З.Ы. 5983 можно прикупть на eBay баксов за 7
З.Ы.Ы. VRBrain такая же дорогая поделка как и PX4 - не вижу смысла переплачивать за такие девайсы.
З.Ы.Ы. VRBrain такая же дорогая поделка как и PX4 - не вижу смысла переплачивать за такие девайсы.
ну как бэ плата то есть конкурентная и явно дешевле 😃 а вот 5983 можно повесить на свободный spi (это в текущей версии), а 5883 можно и отключать программно или заменить вроде подключение одинаковое если по i2c…
и похоже развод это, зачем 5983 цеплять по i2c да пусть к той же mpu?
Я тоже думаю что причина не в УАРТЕ, и время цикла правильное что приходит на ум 22милисекунды - какой-то баг по гире? системное время в высшем приоритете!
да и первый уарт работает на прямую с памятью, не отвлекая жирный проц 😃 - как бы не разленился 😃
Вот тут делаю измерение-
LED3_ON;
serialCom();
LED3_OFF;
Измеряю время работы функции обмена. Меряем на светодиоде. Когда происходит чтение реальных данных из гуи, то имеем время 19мс. А обработка обмена у нас висит как раз в основном цикле. Частота этих багов соответствует частоте опроса из гуи. Интересно что б кто нибудь проверил. Проверять только в режиме чтения реальных данных в вингуи или в мультивий-конфигураторе.
this code is executed at each loop and won’t interfere with control loop if it lasts less than 650 microseconds
т.е. если бы оно тормозило цикл это было бы видно в гуи, и уж у Таймкопа аппараты точно падали при подключении к гуи, да и вий бы никуда не полетел, хотя если мы допустим из 400, расчётов потеряем даже 60% -70% ничего не случится, скоро предоставится возможность всё перепроверить - проверю обязательно… сейчас тупо лень с аппарата плату снимать 😃
т.е. если бы оно тормозило цикл это было бы видно в гуи, и уж у Таймкопа аппараты точно падали при подключении к гуи, да и вий бы никуда не полетел, хотя если мы допустим из 400, расчётов потеряем даже 60% -70% ничего не случится, скоро предоставится возможность всё перепроверить - проверю обязательно… сейчас тупо лень с аппарата плату снимать 😃
Таймкоп не причем помойму.
Вот тут проблемка
void uartWrite( uint8_t ch)
{
LED3_ON;
while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE) {
};
LED3_OFF;
txBuffer[txBufferHead] = ch;
Цикл ожидания равен примерно 87 мкс. И это на каждом байте. Можете прикинуть тормоза системы при усиленном обмене. Тут чето с дма не то.
хех… ну как я и говорил - проблемка в уарте, а не в функции serialcom
если его убрать УАРТ вешается, или отказываться от ДМА или добивать этот чёртов уарт, по всей видимости переполняется буфер ДМА ибо пихаем слишком быстро в него, я как и писал выше воткнул этот костыль, ибо не нашел ошибку, при этом уарт на приём работает, а на выдачу затыкается,
первая версия была такая:
// 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();
}
}
void uartWrite( uint8_t ch)
{
LED3_ON;
while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE) {
};
LED3_OFF;
txBuffer[txBufferHead] = ch;Цикл ожидания равен примерно 87 мкс. И это на каждом байте. Можете прикинуть тормоза системы при усиленном обмене. Тут чето с дма не то.
Верно, это косяк! Смысл использования ПДП как раз в том чтобы ничего не ждать! uartWrite должна вычислять хвост буфера ПДП и класть символ в [ХВОСТ+1], и увеличивать счетчик ПДП на один. Если ХВОСТ+1 упирается в конец буфера, то возвращать признак переполнения, по нему пропускать вывод в порт, либо ждать завершения с циклом счёта. А ещё лучше сделать кольцевой буфер и при записи писать по кругу, и только если догнал хвост возвращать переполнение. Важно перед операциями с регистрами ПДП запрещать запрос канала.
кстати такой вопрос…
а в чем глобальный смысл использовать ДМА для уарта?
разницу в использовании процессора наврятли заметим - уарт работает без участия ЦПУ, а по времени засунуть байт в уарт или в ДМА -> уарт думаю что разницы не будет.
единственная разница будет в том случае, если ДМА будем передавать весь буфер целиком.
так что без переработки этого участка плюсов в ДМА не вижу.
Короче без кольцевого тут не обойтись.
а в чем глобальный смысл использовать ДМА для уарта?
правильный вопрос! объем данных и частота передачи не высокие. Кольцевой буфер на прерываниях никак не замедлит проц.
Короче без кольцевого тут не обойтись.
это оптимальное решение.
легко проверить - перенаправить всё во 2-й уарт (он без ДМА) если всё канает то и убирать ДМА к чертям, получается Ф4 пихает в UART столько, что тот не может пережевать, и увеличение буфера да и кольцевой тут не помогут - всё равно рано или позно наступит переполнение.
во всех файлах где есть uart1 заменить на 2, остальное вроде идентично - третий по умолчанию gps…
а если при переполнении буфера его тупо очищать - потеря нескольких пакетов ну как-то вообще ничего не испортит?
у меня сейчас есть некоторые затруднения - сам проверить ничего не могу 😦
ИМХО, в данной ситуации, надо дропать пакеты. Протокол ессесно должен уметь это переваривать (ну т.е. ничего страшного не должно произойти если что то не дойдет до адресата).
будет писать в счётчик ошибок пакетов в гуи - только и всего…
вот это работает, только не знаю с ДМА или без, просто прошивку заливал себе проверить порт: 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
Короче мне так кажется - нужно loop садить куда нибудь в прерывание.
Да, алгоритм стабилизации в первую очередь…, и других прерываний как можно меньше стараться делать тогда время цикла стабильно.
Тем более все успевает обработаться включая ПИД даже на моих 72Мгц (не говоря уж 168)…
все успевает обработаться включая ПИД даже на моих 72Мгц (не говоря уж 168)…
У меня на Ф1 без аппаратной плавучки всё успевало работать в прерываниях.
Приём по юсарту я использую в прерываниях, передачу - через 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
чтобы удостовериться что данные точно ушли, ибо конец передачи дма не означает конец передачи данных, и запись новых данных приводит к потерям.