Jump to content

    

sKWO

Участник
  • Content Count

    354
  • Joined

  • Last visited

Community Reputation

0 Обычный

About sKWO

  • Rank
    Местный
  • Birthday 10/25/1981

Контакты

  • Сайт
    Array

Информация

  • Город
    Array
  1. Я дма использую только для передачи. Для приёма лучше ложить в фифо а дальше с очередями работать и не мучиться с дма. ДМА удобно по приёму например с АЦП но не для приема данных с серийного интерфейса, ИМХО. Вот повтор кода который я выкладывал: // 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; } } } сначала проверили или по приему сюда попали а дальше или каллбэк на функцию или байт в фифо. так удобнее.
  2. попробуйте так "if ((usart1_rx_buff[0]=='F')&&(usart1_rx_buff[1]=='N')&&(usart1_rx_buff[2]=='0')&&(usart1_rx_buff[3]=='1'))" Вам нужно не побитное "и" а логическое. или так //поиск подстроки в строке с ограничением по длине поиска __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; } вот вам пример для вашего контроллера с фифо и дма хедер #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); драйвер /* * 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; } } }
  3. Хотите изврата, тогда ладно. Как вариант вам нужен режим Fast PWM. Используйте два прерывания по переполнению таймера 1 и по совпадению OCIE1А или OCIE1B, тут разницы нет. Если св.диоды включаются нулем, то в прерывании по совпадению настраивайте Ваш порт на выход. В преривании по переполнению Т1 Ваш порт на вход. Для игрушки в самый раз.
  4. скорее первое чем второе ибо симулинк фореве
  5. Применяйте COBS алгоритм, поищите в разделе АВРов. Правда нужно будет использовать паузы между пакетами, но то мелочи… Если используете радиоканал, то самосинхронизирующийся код Манчестера в помощь. Очень хорошая статья. В своё время решила много моих вопросов. сеть сбора даных
  6. по напряжению с трудом, по току нет. Выбирайте транзистор с импульсным током 3А(рабочий Ваш)*2(или на 3 с запасом). На пределе будет работать КТ829. Для ключевых цепей лучше полевик напр. фет., самый распространённый IRF530 Вам с головой. Да и частота в пределах от 50....150КГц
  7. В своё время мне помогла вот эта ветка, сама идея заключалась в дополнительной проверке флага переполнения. Я прав, она? А именно && (TIFR & (1<<OCF)
  8. на самом деле EPM3032 есть в двух видах корпусов с 44мя выводами: 44–Pin PLCC и 44–Pin TQFР, причём максимальное количество пользовательских выводов для обеих корпусов - 34. Существует мнение что если вам надо больше чем одна микросхема 74хх то можно использовать CPLD. направлене перспективное и если нельзя найти необходимую логику, а единичный девайс надо сделать быстро то CPLD-шки становятся палочкой выручалочкой. наслаждение от творчества и новые гораздо быстрее реализуемые задачи чем схемотехника на рассыпухе.
  9. Не совсем понятно суть вопроса. посмотрите в сторону Specifying vectors. подробно для верилога написал des00, для VHDL должно быть также. смотрите http://electronix.ru/forum/index.php?showt...=20307&st=0
  10. Да, бидики прямо на каждый затвор фета. вот EMI фильтры от мурата там расписано об эфективности работы на разных частотах Не избавит совсем, наличие резистора ИМХО это скорее правило хорошего тона. Он играет роль скорее для ограничении тока заряда затворной ёмкости, или рассеивания на нём мощности если ток драйвера слишком мал. более детально тут
  11. Как один из вариантов - да. таки -да. автору нужно поставить 1% резисторы ом скажем от 5-ти до 15-ти это избавит от звона и чуточку замедлит коммутацию фетов. можно ещё поставить ферритовые биды, хотя корпус не тот. должен признать что емкость затвора весьма относительное, там и индуктивность и сопротивление. если посмотреть на кривую зависимости заряда от напряжения в этом можно убедиться.
  12. static __flash UINT8 No_ErazeData[] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 ................................................ ................................................... };
  13. скептическое настроение нащёт параллельного включения фетов прошло.осадок остался... у топик-стартера затворы соединены через резисторы с разбросом сопротивления больше чем разброс ёмкостей у тех же фетов. подозреваю что в моменты комутации кто-то раньше тянет одеяло на себя ну а кто-то позже (в зависимости от тау цепи). именно это меня и смутило. если биполярники выравниваютя в эмитерах нихромовыми нитями то в цепях комутации феты ИМХО надо соединять ного к ноге и чем ближе тем лучше. выравнивание же резисторами на затворах, насколько мне не изменяет память - это из области линеаризации звучания УМЗЧ где в выходных каскадах использованы ключевые транзисторы каковыми и придуманы феты всё очень просто - неравномерный нагрев - это и есть следствие выравнивания по току. расбросс у фетов небольшой но всё-же он таки есть. "выравнивание" получается вследствии того что фет с меньшим сопротивлением И-С греется больше чем фет(ы) с большим сопротивлением, а разогрев канала приводит к увеличению его сопротивления и как следствие уменьшение тока протекающего через него и его увеличения через другие транзисторы. иными словами происходит тепловое выравнивание сопротивлений транзисторов. соответственно медная пластина и термопаста должна улучшить положение для улучшения самовыравнивания.
  14. Большой ток МОСФЕТА достигается за щёт каскадирования нескольких полевиков в одном корпусе,это получается при "подгорании" одного или нескольких транзисторов. Таки да. Кроме того что Вы параллельно включили МОСФЕТЫ для достижения нужного тока И-С он то у Вас и выгорает. А выгорает почему - ток большой, транзисторов на его пропуск "пашет много и кого-то сдают нервы". из Вики: MOSFET - полевой транзистор с изоляцией затвора окислом кремния - соответственно он управляется как полевик напряжением и при превышении макс. напряжения на затворе тоже может приводить к подгоранию и приводит. Прочитав топик, ИМХО, каскадировать каскадированое я бы не стал, а посмотрел бы впринцыпе в сторону IGBT, хоть дороже но и напряжение повыше ну и ток поприличнее.
  15. IAR BootSection

    вот попробовал поиском: http://electronix.ru/forum/lofiversion/ind...hp/t115651.html