Перейти к содержанию
    

Михась

Участник
  • Постов

    370
  • Зарегистрирован

  • Посещение

Сообщения, опубликованные Михась


  1. Усиливаем переменку ОУ, далее на МК с АЦП, который тянет мегагерц. АЦП кладет в ОЗУ через ДМА. Через ДМА же передаем по SPI на 8 или 16 мегабит. Среда передачи по SPI - POF (что-то подобное AFBR-2624Z). Будут два волокна - одно для клока, второе для данных. Питание этой изолированной части - можно от батарейки.

    На приемной части - МК с двумя SPI для приема. Передача на ПК по вкусу - можно через  USB например.

  2. Только авиамодельный двигатель и к нему колхозить генератор через редуктор. Или использовать в качестве безредукторного генератора высокоскоростной двигатель с постоянными магнитами. 

     

    Если нужен мощный - то смотреть на парамоторы.

    • Upvote 1
  3. Сугубое ИМХО - импульсный метод, две катушки. На одну высоковольтный импульс, на другой смотрим отклик. На первый взгляд реально. Но характеристики маловероятно что кто-то подскажет, нужно экспериментировать. Давно делал макет импульсного промышленного МД, но там все осталось на уровне макета, заказчик отвалился. Только катушка была побольше и кусочки металла побольше.

  4. CH32F207 Защита цепей 10BASE-T.

    Собственно вопрос, закончили отработку схемы схемы и ПО на этом МК со встроенным PHY. Но нет опыта защиты цепей трансформатор-PHY. ОТ производителя есть абстрактная рекомендация использовать TVS, но марки не говорят. Может кто из форумчан посоветует доступные TVS?

    Eth.pdf.jpg

  5. Делал выделение производной от бародатчика. Для сравнения один и тот-же поток данных прогонял через Калмана и Альфа-Бета фильтры параллельно и сравнивал графики. В общем результат был одинаковый, когда подогнал эмпирически коэффициенты, но Калман получился процентов не 10 побыстрее. В общем, чужой опыт тут скорее всего не поможет.

  6. 3 hours ago, MrYuran said:

    Опять же, какая вероятность, что отверстие...

    Пф. У нас в офисе что-то прикручивали к стене и попали в горизонтальный провод. Бахнуло, вскрыли проводку, соединили. 

    Приняли решение перенести дюбели на 10 сантиметров выше. Первый нормально, второй опять бахнул. Оказалось провод шел не по горизонтали а немного с наклоном вверх.

  7. Ну и тогда про стандарты - TFTP в базе не умеет удалять файлы, на сколько я вспомнил. А при маленьком объеме файловой системы это уже очень важно. Можно обойти заливкой специального командного файла, который содержит имя удаляемого файла. Но это уже будет нестандартная надстройка над TFTP.

    И еще надо подумать, что бутлоадер помимо стека TCP/IP потянет за собой файловую систему.

     

  8. 54 minutes ago, makc said:

    Уже лучше, когда в последний раз к ним смотрел ничего этого ещё не было. Правда там тоже полно ляпов, достаточно крупных. Сначала пишут MIK32:

    Всякого такого и у Миландра хватало. Как обычно кто-то пишет, а вычитать свежим взглядом некому. Есть еще метод - надо выводить документ с вотермаркой DRAFT. И кто найдет косяк, пусть пишет на почту - получит бесплатную девборду.

  9. 1 hour ago, makc said:

    Начать хотя бы с того, что у Миландра на них хотя бы есть спецификации. С белыми пятнами, местами кривые, но есть и можно заниматься разработкой. А где такие спецификации в открытом доступе на АМУР? Куцая Wiki и все? 🫢

     

    Вот https://mikron.ru/products/iot/mk32-amur/#!/tab/672102497-3

  10. Это библиотечная функция.

    void ADC_StartOfConversion(ADC_TypeDef* ADCx)
    {
      ADCx->CR |= (uint32_t)ADC_CR_ADSTART;
    }

    В общем, проблема, как я думаю, не в DMA. Будем смотреть дальше, может в скаде. Потому как просто сдвиг, это понятно. А сдвиг по кольцу - это уже не понятно.

    Остальной код - это про другое.

     

  11. 48 minutes ago, Arlleex said:

    Почему adc_raw не volatile?
     

    DMA-то да, перезапускать не надо. Вот только АЦП может после N преобразований скинуть бит активации и ждать следующего "пинка". Я не вдавался в подробности исходника ТС, возможно у него так (полагаться на простыни SPL/HAL не хочу).

    На сколько помню, так и есть.

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

    Прибор имеет 8 каналов АЦП (STM32F030), которые преобразовывают напряжение в температуру. Произошел сбой порядка чтения температур на один канал, т.е данные со второго канала попали в первый и т.д. 

    Грешу на сбой DMA, т.к. все остальные преобразования производятся последовательным вызовом одной функции без статических переменных. После сброса прибора показания нормализовались, до этого он не выключался примерно несколько месяцев.

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

     

    ***********************************************************

    Версия с DMA не подходит, т.к. в массив температур в этом случае попали бы коды температуры процессора и ИОН!

    **********************************************************

     

    Spoiler
    /*----------------------------------------------------------------------------
     * Name:    12ADC.с
     * Purpose:
     * Note(s):
     *----------------------------------------------------------------------------
    
    
     *----------------------------------------------------------------------------*/
    #include "stm32f0xx_conf.h"
    #include "stm32f0xx.h"
    #include "stm32f0xx_it.h"
    
    #include "12ADC.h"
    
    //-------- Работа с АЦП --------------------------------------------------------
    // ДМА кладет первый отсчет регулярного канала в 1 ячейку,
    // видимо есть неочищенные флаги при запуске
    
    
    // 8 - на отсчеты adcin A0-A7
    // 2 - VREF + internal TermoDat
    #define SIZEADCBUFF (8+2)		// размер буфера данных АЦП 
    
    #define ADC1_DR_Address    ((uint32_t)0x40012440)
    #define CODE_FULL 4095 			// АЦП 12 бит 
    
    // local
    static uint16_t adc_raw[SIZEADCBUFF];
    static volatile bool endconversion;
    
    /*******************************************************************************
    * Режим АЦП, когда данные по нескольким каналам передаются через DMA напрямую
    в ОЗУ
    * АЦП запускается по команде ADC_StartOfConversion(ADC1);
    * Генерируется прерывание обработчика массива
       Parameter:
    *  Return:
    Тактирование - 14 мгц от внутр.  RC генератора HSI14
    14М/239 = 58577 Гц - частота семплирования для одного канала (17.1uS)
    Для 10 каналов результат будет с частотой 5857 Гц или каждые 170uS
    При времени семплирования 239.5 периодов входное сопротивление 50 килоом
    *******************************************************************************/
    void ADC12_init(void)
    {
    	GPIO_InitTypeDef  				GPIO_InitStructure;
    	ADC_InitTypeDef 					ADC_InitStructure;
    	DMA_InitTypeDef 					DMA_InitStructure;
    	NVIC_InitTypeDef	 				NVIC_InitStructure;
    
    	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
    
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | \
    	                              GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | \
    	                              GPIO_Pin_6 | GPIO_Pin_7;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    	/* DMA1 channel1 configuration ----------------------------------------------*/
    	DMA_DeInit(DMA1_Channel1);
    
    	DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
    	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adc_raw;
    	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    	DMA_InitStructure.DMA_BufferSize = (SIZEADCBUFF);
    	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
    	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    
    	// Включим прерывание от ДМА по окончанию передачи блока
    	DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); // весь блок
    	NVIC_EnableIRQ(DMA1_Channel1_IRQn);
    
    	DMA_Cmd(DMA1_Channel1, ENABLE);
    	ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular);
    	ADC_DMACmd(ADC1, ENABLE);
    
    	NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    	NVIC_InitStructure.NVIC_IRQChannelPriority = 2; 	//наименьшему число соответствует больший приоритет
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    	NVIC_Init(&NVIC_InitStructure);
    
    	ADC_StructInit(&ADC_InitStructure);
    
    	ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
    	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    	ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    	ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward;
    	ADC_Init(ADC1, &ADC_InitStructure);
    
    	/* ADC1 regular channel configuration */
    	ADC_ChannelConfig(ADC1, ADC_Channel_0, ADC_SampleTime_239_5Cycles); 						// PA0
    	ADC_ChannelConfig(ADC1, ADC_Channel_1, ADC_SampleTime_239_5Cycles); 						// PA1
    	ADC_ChannelConfig(ADC1, ADC_Channel_2, ADC_SampleTime_239_5Cycles); 						// PA2
    	ADC_ChannelConfig(ADC1, ADC_Channel_3, ADC_SampleTime_239_5Cycles); 						// PA3
    	ADC_ChannelConfig(ADC1, ADC_Channel_4, ADC_SampleTime_239_5Cycles); 						// PA4
    	ADC_ChannelConfig(ADC1, ADC_Channel_5, ADC_SampleTime_239_5Cycles); 						// PA5
    	ADC_ChannelConfig(ADC1, ADC_Channel_6, ADC_SampleTime_239_5Cycles); 						// PA6
    	ADC_ChannelConfig(ADC1, ADC_Channel_7, ADC_SampleTime_239_5Cycles); 						// PA7
    
    	ADC_ChannelConfig(ADC1, ADC_Channel_TempSensor, ADC_SampleTime_239_5Cycles);
    	ADC_ChannelConfig(ADC1, ADC_Channel_Vrefint, ADC_SampleTime_239_5Cycles);
    
    	ADC_TempSensorCmd(ENABLE); 						// температурный датчик
    	ADC_VrefintCmd(ENABLE);								// ИОН
    
    	ADC_GetCalibrationFactor(ADC1);
    	ADC_Cmd(ADC1, ENABLE);
    	ADC_StartOfConversion(ADC1);
    	ADC_DMACmd(ADC1, ENABLE);
    
    	// Далее АЦП будет работать в автоматическом режиме
    	// готовые данные по DMA складывать в массив в ОЗУ.
    }
    
    static uint16_t flash_read(uint32_t address)
    {
    	return (*(__IO uint16_t*) address);
    }
    
    /*******************************************************************************
    * Измерение напряжения питания AVCC
    *   Parameter:	Код с канала внутреннего ИОН (можно отфильтровать)
    *   Return:   	Напряжение питания АЦП AVCC в мВ
    *******************************************************************************/
    static int16_t get_AVCC(uint16_t Vrefint_code)
    {
    	// Читаем корректировочный код VREF при 3.3В из FLASH
    	uint16_t VREF_CAL = flash_read(0x1FFFF7BA);
    	uint16_t AVCC = (VREF_CAL * 3300) / Vrefint_code;
    	return AVCC;
    }
    
    /*******************************************************************************
    * Преобразование кода канала в напряжение после делителя
    		Parameter:
    		Return: напряжение в милливольтах
    *******************************************************************************/
    static uint16_t get_vCn(uint16_t cn_code, int16_t avcc)
    {
    	uint32_t voltage = ((uint32_t)cn_code * avcc) / CODE_FULL;  // напряжение в мВ без делителя
    	return (uint16_t)voltage;
    }
    
    
    /*******************************************************************************
    * Измерение температуры на сенсоре CPU
    Parameter:  код канала температуры, значение питания АЦП в милливольтах
    Return: температура в градусах цельсия
    *******************************************************************************/
    //#define TEMP110_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7C2))
    //#define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7B8))
    //#define VDD_CALIB ((uint16_t) (330))		// для F030 измерения при 3.3В
    
    //static int16_t get_temperatureCPU_051(int16_t code_temper, int32_t AVCC)
    //{
    //	int32_t temperature = ((code_temper * (AVCC / 10) / VDD_CALIB) - (int32_t)(*TEMP30_CAL_ADDR));
    //	temperature = temperature * (int32_t)(110 - 30);
    //	temperature = temperature / (int32_t)(*TEMP110_CAL_ADDR - *TEMP30_CAL_ADDR);
    //	temperature = temperature + 30;
    //	return (int16_t)temperature;
    //}
    
    /* Temperature sensor calibration value address */
    #define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7B8)) // сохраненный результат в кодах АЦП при 30грС
    #define VDD_CALIB 	(3300) // в милливольтах
    #define AVG_SLOPE 	(4300) // в экземпле 5336 в даташите 4300
    //AVG_SLOPE in ADC conversion step (@3.3V)/°C multiplied by 1000 for precision on the division
    
    static int16_t get_temperatureCPU_030(int16_t code_temper, int32_t AVCC)
    {
    	int32_t temperature = ((uint32_t) *TEMP30_CAL_ADDR - ((uint32_t)code_temper * AVCC) / VDD_CALIB) * 1000;
    	temperature = (temperature / AVG_SLOPE) + 30;
     	return (int16_t)temperature;
    }
    
    /*******************************************************************************
    * Получение всех параметров из кода АЦП
    *   Parameter:
    напряжение канала в милливольтах
    температура кристалла в градусах
    напряжение на CPU в милливольтах
    *   Return:
    *******************************************************************************/
    void meas_analog12(int32_t * Cn0_voltage,
                       int32_t * Cn1_voltage,
                       int32_t * Cn2_voltage,
                       int32_t * Cn3_voltage,
                       int32_t * Cn4_voltage,
                       int32_t * Cn5_voltage,
                       int32_t * Cn6_voltage,
                       int32_t * Cn7_voltage,
                       int16_t * CPU_temp,
                       int16_t * avccCPU)
    {
    	if(endconversion == true)
    	{
    		// вычисляем напряжение питания контроллера
    		uint16_t U_AVCC = get_AVCC(adc_raw[0]);
    		// измеряем напряжение
    		*Cn0_voltage = get_vCn(adc_raw[1], U_AVCC);
    		*Cn1_voltage = get_vCn(adc_raw[2], U_AVCC);
    		*Cn2_voltage = get_vCn(adc_raw[3], U_AVCC);
    		*Cn3_voltage = get_vCn(adc_raw[4], U_AVCC);
    		*Cn4_voltage = get_vCn(adc_raw[5], U_AVCC);
    		*Cn5_voltage = get_vCn(adc_raw[6], U_AVCC);
    		*Cn6_voltage = get_vCn(adc_raw[7], U_AVCC);
    		*Cn7_voltage = get_vCn(adc_raw[8], U_AVCC);
    
    		*CPU_temp = get_temperatureCPU_030(adc_raw[9], U_AVCC);
    		*avccCPU = U_AVCC;
    		endconversion = false;
    
    	}
    	else
    	{
    		*Cn0_voltage = 0;
    		*Cn1_voltage = 0;
    		*Cn2_voltage = 0;
    		*Cn3_voltage = 0;
    		*Cn4_voltage = 0;
    		*Cn5_voltage = 0;
    		*Cn6_voltage = 0;
    		*Cn7_voltage = 0;
    		*CPU_temp = 0;
    		*avccCPU = 0;
    	}
    	ADC_StartOfConversion(ADC1);
    }
    
    /*----------------------------------------------------------------------------
      Обработчик прерывания от DMA - АЦП по окончании передачи блока
     *---------------------------------------------------------------------------*/
    void DMA1_Channel1_IRQHandler(void)
    {
    	if(DMA_GetITStatus(DMA1_IT_TC1))
    	{
    		endconversion = true;
    		DMA_ClearITPendingBit(DMA1_IT_TC1);
    	}
    }
    
    //-------------------------------------------------------------------------------

     

  13. On 9/22/2023 at 6:45 PM, x893 said:

    Есть примеры готовых изделий ?

    Аналог их анализатора применяется в медицине.   По сути там картридж- бумажка пропитанная специфическим реагентом, меняющим окраску. В коробочке стоит тупая веб-камера, передает в ПО, которая анализирует изменение окраски, пишет в базу результаты и печатает отчет. Продается государству за сотни тысяч.  

×
×
  • Создать...