|
STM32F4 ADC DMA ? |
|
|
|
Jul 22 2012, 14:14
|
Профессионал
    
Группа: Свой
Сообщений: 1 047
Регистрация: 28-06-07
Из: Israel
Пользователь №: 28 763

|
Помогите пожалуйста. Хочу запустить АЦД непрерывно на оцифровку последовательно 12 каналов, чтобы складывало с массив. Вот код. Не могу понять - после инициализации DMA, в его регисторах все нули. Пробовал заускать программу - в массиве тоже все нули. Код //volatile u16 ADCConvertedValue[12]; <- the results are here void adc_init(void){ ADC_InitTypeDef ADC_InitStruct; ADC_CommonInitTypeDef ADC_CommonInitStruct; DMA_InitTypeDef DMA_InitStructure; // ports are already configured RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); // enable clocking of ADC1 RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_DMA2,ENABLE); // enable clocking of DMA2 //==Configure DMA2 - Channel0 Stream 4== DMA_InitStructure.DMA_Channel = DMA_Channel_0; DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR; DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) &ADCConvertedValue; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_BufferSize = 12; 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_High; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream4, &DMA_InitStructure); DMA_Cmd( DMA2_Stream4, ENABLE); // ADC: ADC_DeInit(); // turn ADC off ADC_CommonInitStruct.ADC_Mode =ADC_Mode_Independent; ADC_CommonInitStruct.ADC_Prescaler =ADC_Prescaler_Div4; ADC_CommonInitStruct.ADC_DMAAccessMode =ADC_DMAAccessMode_Disabled; ADC_CommonInitStruct.ADC_TwoSamplingDelay =ADC_TwoSamplingDelay_5Cycles; ADC_CommonInit(&ADC_CommonInitStruct); ADC_StructInit(&ADC_InitStruct); ADC_InitStruct.ADC_Resolution =ADC_Resolution_12b; ADC_InitStruct.ADC_ScanConvMode =ENABLE; ADC_InitStruct.ADC_ContinuousConvMode =ENABLE; ADC_InitStruct.ADC_DataAlign =ADC_DataAlign_Right;//Left; ADC_InitStruct.ADC_NbrOfConversion =12; ADC_Init(ADC1,&ADC_InitStruct); //----channels order ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1,ADC_SampleTime_480Cycles);// PC0 ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2,ADC_SampleTime_480Cycles);// PC1 ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 3,ADC_SampleTime_480Cycles);// PC2 ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 4,ADC_SampleTime_480Cycles);// PC3 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 5,ADC_SampleTime_480Cycles);// PA0 ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 6,ADC_SampleTime_480Cycles);// PA1 ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 7,ADC_SampleTime_480Cycles);// PA2 ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 8,ADC_SampleTime_480Cycles);// PA3 ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 9,ADC_SampleTime_480Cycles);// PA6 ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 10,ADC_SampleTime_480Cycles);// PA7 ADC_RegularChannelConfig(ADC1, ADC_Channel_14,11,ADC_SampleTime_480Cycles);// PC4 ADC_RegularChannelConfig(ADC1, ADC_Channel_15,12,ADC_SampleTime_480Cycles);// PC5 // enable ADC and DMA: ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); ADC_DMACmd(ADC1, ENABLE); //Enable ADC1 DMA ADC_Cmd(ADC1, ENABLE); //Enable ADC1 ADC_SoftwareStartConv(ADC1); // start ADC conversions } //----------------------------- Проц stm32f407, J-Link, Keil 4.23
|
|
|
|
|
 |
Ответов
(1 - 55)
|
Jul 23 2012, 08:11
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(kan35 @ Jul 23 2012, 11:02)  Вы прошли через то, что путали слова clock и reset? и когда перестали пользоваться библиотеками, то вам помогло?  Нет, до этого не доходило. Но как же трудно было искать в html-документации эти функции, состав структур. Куда логичнее посмотреть содержимое регистров в руководстве на микроконтроллер, и использовать только описание этих битов из stm32f10x.h (в моем случае). И места занимает намного меньше во флэшь-памяти. А, главное, чувствуешь себя хозяином микроконтроллера, а не приемышем каким-то.
|
|
|
|
|
Jul 23 2012, 10:32
|
Профессионал
    
Группа: Свой
Сообщений: 1 047
Регистрация: 28-06-07
Из: Israel
Пользователь №: 28 763

|
Цитата(ViKo @ Jul 23 2012, 09:44)  А не надо библиотеками пользоваться. С чего вдруг?! Цитата Но как же трудно было искать в html-документации эти функции, А зачем их там искать? Честно говоря, я даже не подозревал о существовании "html-документации", поэтому смотрю просто в .с и .h файлах самих библиотк, там зачастую более чем остаточно информации - смотрю в .с функцию, перед ней расписаны ее аргументы, все варианты. В .h смотрю структуру и ее варанты ее параметров.
|
|
|
|
|
Jul 23 2012, 11:15
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(kan35 @ Jul 23 2012, 13:42)  Allregia, абсолютно правильный подход Так-таки "абсолютно"? Я инициализирую, например, порты следующим образом (с помощью своих макроопределений): Код ;;;192 void GPIO_init(void) 000076 4849 LDR r0,|L1.412| ;;;193 { ;;;194 /* Разрешить тактирование портов A, B, C */ ;;;195 RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN; 000078 301c ADDS r0,r0,#0x1c 00007a 6801 LDR r1,[r0,#0] 00007c f0410107 ORR r1,r1,#7 000080 6001 STR r1,[r0,#0] ;;;196 ;;;197 /* Инициализировать GPIOA */ ;;;198 GPIO_INIT(A, 000082 494b LDR r1,|L1.432| 000084 4849 LDR r0,|L1.428| 000086 6008 STR r0,[r1,#0] 000088 2000 MOVS r0,#0 00008a 8088 STRH r0,[r1,#4] 00008c 4a49 LDR r2,|L1.436| 00008e 608a STR r2,[r1,#8] 000090 2214 MOVS r2,#0x14 000092 60ca STR r2,[r1,#0xc] 000094 f44f72b8 MOV r2,#0x170 000098 620a STR r2,[r1,#0x20] 00009a 4a47 LDR r2,|L1.440| 00009c 624a STR r2,[r1,#0x24] ;;;199 MD_GPO, OT_PP, SP_400K, PL_NP, AF_SYSTEM, // PA0 ;;;200 MD_AF, OT_PP, SP_400K, PL_PU, AF_USART2, // PA1 ;;;201 MD_AF, OT_PP, SP_400K, PL_PU, AF_TIM2, // PA2 ;;;202 // MD_GPO, OT_PP, SP_10M, PL_PD, AF_SYSTEM, // PA3 - Test ;;;203 MD_AN, OT_PP, SP_400K, PL_NP, AF_SYSTEM, // PA3 - ADC3 LQFP48 ;;;204 MD_AN, OT_PP, SP_400K, PL_NP, AF_SYSTEM, // PA4 - ADC4 ;;;205 MD_AN, OT_PP, SP_400K, PL_NP, AF_SYSTEM, // PA5 - ADC5 ;;;206 MD_AN, OT_PP, SP_400K, PL_NP, AF_SYSTEM, // PA6 - ADC6 = VAC1 ;;;207 MD_AN, OT_PP, SP_400K, PL_NP, AF_SYSTEM, // PA7 - ADC7 = VAC2 ;;;208 MD_AF, OT_PP, SP_10M, PL_NP, AF_SYSTEM, // PA8 - MSO (USART1) ;;;209 MD_AF, OT_PP, SP_10M, PL_NP, AF_USART1, // PA9 - USART1_TX ;;;210 MD_AF, OT_PP, SP_10M, PL_NP, AF_USART1, // PA10 - USART1_RX ;;;211 MD_AF, OT_PP, SP_10M, PL_NP, AF_USART1, // PA11 ;;;212 MD_AF, OT_PP, SP_10M, PL_NP, AF_USART1, // PA12 ;;;213 MD_AF, OT_PP, SP_10M, PL_NP, AF_SYSTEM, // PA13 - JTMS-SWDAT ;;;214 MD_AF, OT_PP, SP_10M, PL_NP, AF_SYSTEM, // PA14 - JTCK-SWCLK ;;;215 MD_AF, OT_PP, SP_10M, PL_NP, AF_SYSTEM); // PA15 - JTDI ;;;216 ;;;217 /* Инициализировать GPIOB */ и т.д. Взгляните, во что выливается код инициализации при использовании библиотечных функций. Потом посмеемся и поплачем...
|
|
|
|
|
Jul 23 2012, 11:49
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(kan35 @ Jul 23 2012, 14:47)  за идею наверное За нее. За "абсолютно правильный подход". Еще пример. Сравните с кодом из сообщения топикстартера. Не убеждает? Код void ADC_init(void) { /* Разрешить тактирование ADC1 */ RCC->CR |= RCC_CR_HSION; // Такты на преобразователь RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // Такты на интерфейс
// ADON == 0 // /* 12-bit , Power Down during Idle phase, Scan Mode */ ADC1->CR1 = ADC_CR1_PDI | ADC_CR1_SCAN; /* Sample Time 16 cycles (1 us) for all channels */ ADC1->SMPR1 = ADC1->SMPR2 = ADC1->SMPR3 = 0x2<<27 | 0x2<<24 | 0x2<<21 | 0x2<<18 | 0x2<<15 | 0x2<<12 | 0x2<<9 | 0x2<<6 | 0x2<<3 | 0x2<<0; /* Right alignment, DMA mode disabled, No delay, ADC On */ ADC1->CR2 = ADC_CR2_ADON; /* Wait while !ADONS */ while (!(ADC1->SR & ADC_SR_ADONS)); // ADONS == 1 // /* External Trigger on the rising edge for injected channels, TIM7_TRGO event */ // ADC1->CR2 = ADC_CR2_JEXTEN_0 | ADC_CR2_JEXTSEL_3 | ADC_CR2_JEXTSEL_1; ADC1->CR2 |= ADC_CR2_JEXTEN_0 | ADC_CR2_JEXTSEL_0; // TIM9_TRGO /* Injected channels data offset = 0 */ /* LQFP64 - 4 injected conversions, 15, 14, 7, 6 channels */ // ADC1->JSQR = 0x3<<20 | 15<<15 | 14<<10 | 7<<5 | 6<<0; /* LQFP48 - 4 injected conversions, 8, 3, 7, 6 channels */ ADC1->JSQR = 0x3<<20 | 3<<15 | 8<<10 | 7<<5 | 6<<0; /* Читать регистры ADC_JDRx, когда установится бит JEOC */ }
|
|
|
|
|
Jul 23 2012, 11:51
|
Профессионал
    
Группа: Свой
Сообщений: 1 047
Регистрация: 28-06-07
Из: Israel
Пользователь №: 28 763

|
Цитата(ViKo @ Jul 23 2012, 13:15)  Так-таки "абсолютно"? Я инициализирую, например, порты следующим образом (с помощью своих макроопределений): Может еще предложите (не дай бог, конечно) на ассемблере писать?  Цитата Взгляните, во что выливается код инициализации при использовании библиотечных функций. Потом посмеемся и поплачем... А кого это .. волнует? Сколько занимает обьем инициализации, по отношению к остальной програме? 0.01% ?  Я лично не хочу вообще думать от всяких иициализациях процессора и тому пдобной ерунде, важнее сама задача.
|
|
|
|
|
Jul 23 2012, 12:46
|
Профессионал
    
Группа: Свой
Сообщений: 1 047
Регистрация: 28-06-07
Из: Israel
Пользователь №: 28 763

|
Цитата(ViKo @ Jul 23 2012, 14:10)  А на это отвечу - Не преуменьшайте. Подумайте хотя бы раз. Посмотрите, сколько флэш-памяти съедает инициализация, сообщите конкретную цифру. Конкретно указанная выше функция занимет 70 байт. Я наверное просто лопнул бы от счастья сьекономить на ней 10-20 байт.... И даже если бы таких инициализаций было бы полсотня (что в 5 раз болше чем в реале) - подумать только целый килобайт бы сьекономил! Ну был бы это PIC12 или 16 я бы еще подумал.... (справедливости ради - 70 это сама функция, вызываемые ею библиотечные - еще 200, тоже офигенная экономия). Цитата Думаю, что и под 10% можно отхватить, для микроконтроллеров с малым объемом. Если Вы не заметили, у используемого мною МК меньше чем с 512кБ в этой серии и не бывает. Конкретная задача у меня сейчас заняла около 70кб. Цитата(ViKo @ Jul 23 2012, 14:37)  "Цифру, сестра! Цифру!" (с) Какую "цифру"? Там Ваш текст а не цифры. Вот и текст этот - "Жуть и Кошмар"  P.S. Но примечательно другое - я написал сообщение вчера днем. И за весь вечер и все утро не нашлось ни одного человека. который бы чем-то помог. Зато когда я сам написал что разобрался - тут сразу советчиков стало намного больше  Короче, давайте на этом закончим.
|
|
|
|
|
Jul 23 2012, 12:48
|
Знающий
   
Группа: Участник
Сообщений: 537
Регистрация: 22-02-06
Пользователь №: 14 594

|
ViKo, Оптимизация ради оптимизации приносит только потерю вашего времени к сожалению. В итоге получаете код плохочитаемый, плохопереносимый - поменять канал таймера например - целая история. А вообще люди пишут свои USB библиотеки, TCP стеки, Оси, да что там говорить - компиляторы пишут  . Так что на этом фоне работать с регистрами это не такой уж страшный велосипед (или фобия).
Сообщение отредактировал kan35 - Jul 23 2012, 12:53
|
|
|
|
|
Jul 23 2012, 12:58
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(Allregia @ Jul 23 2012, 15:46)  Конкретно указанная выше функция занимет 70 байт. Я наверное просто лопнул бы от счастья сьекономить на ней 10-20 байт.... Когда я начинал программировать STM32F103, инициализация с использованием библиотеки заняла у меня что-то около 2,7 KB. Меня это удивило. Ваша функция adc_init() занимет 70 байтов? Не желаете ли сделать еще одно усилие, и посчитать в листинге? Было бы интересно узнать, сколько же заняла. Моя ADC_init() заняла 80 байтов. Цитата P.S. Но примечательно другое - я написал сообщение вчера днем. И за весь вечер и все утро не нашлось ни одного человека. который бы чем-то помог. На это есть очевидная причина.  Цитата(kan35 @ Jul 23 2012, 15:48)  Так что на этом фоне работать с регистрами это не такой уж страшный велосипед (или фобия). Так это мне в плюс или в минус?
|
|
|
|
|
Jul 23 2012, 19:38
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(kan35 @ Jul 23 2012, 16:30)  Это конечно все лирика, но все же доверять только собственным исходным кодам практика довольно пагубная для инженера, согласитесь. Лично я не сталкивался (не помню), но на форуме проскакивали сообщения, где говорилось про ошибки в библиотеке. А чтобы найти такие ошибки, придется и по функциям пройтись, и по регистрам. Вот это, действительно, кошмар.
|
|
|
|
|
Jul 23 2012, 20:04
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(kan35 @ Jul 23 2012, 16:30)  Это плюс несомненно. А вообще, вы задавались вопросом "почему появились библиотеки периферии" - очень просто: периферия стала жутко сложная, регистры 32 битные - битов дополна. И программировать их напрямую как минимум долго, неудобно и надо постоянно рыться в референсмануале на 1000 страниц.
Это конечно все лирика, но все же доверять только собственным исходным кодам практика довольно пагубная для инженера, согласитесь. О сложностях, кстати. Да, периферия стала сложная поэтому не надо ее еще усложнять дополнительными абстракциями. Для меня, например, не постижимо по названию функции TIM2_Init понять зачем она нужна и что делает. Запомнить ее назначение можно только постоянно с ней имея дело (вынужденно, поскольку дольше приходится писать инициализацию) и детально просмотрев ее код, поскольку здесь нет ничего интуитивно понятного. Гораздо проще найти в мануале описание таймера где все регистры описаны на паре страниц, и записать этот десяток регистров используя битовые маски. Заимствовать исходники нужно когда они выполняют действительно объемные задачи. А здесь пару строк записи в регистры заменяют огромными библиотеками.
|
|
|
|
|
Jul 24 2012, 04:10
|
Знающий
   
Группа: Участник
Сообщений: 537
Регистрация: 22-02-06
Пользователь №: 14 594

|
Цитата(AlexandrY @ Jul 24 2012, 00:04)  Для меня, например, не постижимо по названию функции TIM2_Init понять зачем она нужна и что делает. Запомнить ее назначение можно только постоянно с ней имея дело (вынужденно, поскольку дольше приходится писать инициализацию) и детально просмотрев ее код, поскольку здесь нет ничего интуитивно понятного. Гораздо проще найти в мануале описание таймера где все регистры описаны на паре страниц, и записать этот десяток регистров используя битовые маски. Резонное замечание. Однако для того, чтобы впадать в отчаяние видя гору функций с непонятными именами (хотя на мой взгляд достаточно понятными) можно обратиться к примерам, которые включены в библиотеку, а так же включен пустой шаблон (template) от которого можно быстро стартовать. В этих примерах разобраны наиболее востребованные режимы работы периферии, например, для ADC приведено кажется более 10 способов обслуживания. Просто копируете оттуда нужный код и его модифицируете. Библиотека selfdocumented и с помощью "go to definition" можно быстро прыгнуть к поснению. Есть еще мощная штука - "Find in files" :-) - быстро все ставит на свои места
|
|
|
|
|
Jul 24 2012, 06:51
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Я использую библиотеки и могу сказать, что они слегка идеологически недоделанные, т.е. приходится самому реализовывать некоторый удобный и очевидный для меня функционал. Например: CODE static void uart_init_rcc(uart_t* const uart) { switch ((uint32_t)uart->sfr) { case USART1_BASE: RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); break; case USART2_BASE: RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); break; case USART3_BASE: RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); break; #ifdef USART4 case USART4_BASE: RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART4, ENABLE); break; #endif
#ifdef USART5 case USART5_BASE: RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART5, ENABLE); break; #endif default: return; }
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // Enable AFIO clock }
static void uart_init_nvic(uart_t* const uart) { switch ((uint32_t)uart->sfr) { case USART1_BASE: NVIC_EnableIRQ(USART1_IRQn); break; case USART2_BASE: NVIC_EnableIRQ(USART2_IRQn); break; case USART3_BASE: NVIC_EnableIRQ(USART3_IRQn); break; #ifdef USART4 case USART4_BASE: NVIC_EnableIRQ(USART4_IRQn); break; #endif
#ifdef USART5 case USART5_BASE: NVIC_EnableIRQ(USART5_IRQn); break; #endif default: return; } } Работа с GPIO у меня полностью своя, кочующая из проекта в проект, независимо от архитектуры контроллера. Считаю что если что-то решается гладко при помощи библиотеки стоит так и решать, ибо инициализация - это, так сказать, разовая акция и на скорость критически влиять не может. А что то до экономии пары сотен байт байт то это смешно. И аргументы насчёт непонятных названий библиотечный функций тоже для меня не понятны...
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Jul 24 2012, 09:48
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
А почему все зациклились на инициализации? Разве библиотека не навязывает использование ее же в обработчиках прерываний и вообще в любой точке кода, в которой нужно обращение к периферии? Интересно узнать от использующих библиотеку, во что выливается библиотечная функция чтения флага прерывания? Во что выливается сброс флага или двух флагов? Без библиотеки я знаю, чего мне это стоит: CODE 151:././../common/rf/transmitter.cpp **** DMA1->IFCR = DMA_IFCR_CHTIF3 | DMA_IFCR_CTCIF3; 1027 .loc 1 151 0 1028 003c 3C4B ldr r3, .L76+8 @ tmp165, 1029 003e 4FF4C062 mov r2, #1536 @ tmp166, 1030 0042 5A60 str r2, [r3, #4] @ tmp166, MEM[(struct DMA_TypeDef *)1073872896B].IFCR Могу ли я позволить себе тут полноценный вызов функции с подготовкой ее параметров и прочими накладными расходами? Наверное конкретно в этом случае могу, даже несмотря на то, что это обработчик прерывания. Но этот вызов будет лишь способствовать глобальному потеплению. А где-то эта лишняя задержка может уже существенно ударить по энергопотреблению или производительности конечного устройства. Полагаю, что все эти наши споры происходят лишь по одной причине - если бы обращения к библиотеке компилировались бы в такие же обращения к регистрам без лишних накладных расходов, то противников библиотеки практически не осталось бы. Я бы сам первый начал ее использовать. И чем больше людей бы ее использовали, тем быстрее в ней нашлись бы все (почти  ) ошибки. Но пока добавляемые библиотекой накладные расходы лично для меня неприемлемы. Кстати, будет интересно посмотреть, во что выливается DMA1->IFCR = DMA_IFCR_CHTIF3 | DMA_IFCR_CTCIF3; при использовании библиотеки. P.S. Вообще все эти войны С/Асм, С/С++, Регистры/библиотека очень напоминают замечательный мультик: http://www.youtube.com/watch?v=JMJXvsCLu6s...player_embedded
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jul 24 2012, 17:42
|
Профессионал
    
Группа: Свой
Сообщений: 1 047
Регистрация: 28-06-07
Из: Israel
Пользователь №: 28 763

|
Цитата(Сергей Борщ @ Jul 24 2012, 11:48)  А почему все зациклились на инициализации? Разве библиотека не навязывает использование ее же в обработчиках прерываний и вообще в любой точке кода, в которой нужно обращение к периферии? Не знаю как другим, а мне - не навязывает. Я пользуюсь библиотеками, но там где time critical и надо шевелить ножками или флагами, пишу обычно типа: Код __INLINE void TestPinOn(void) { GPIOD->BSRRL = GPIO_Pin_15; } // 1=on, __INLINE void TestPinOff(void) { GPIOD->BSRRH = GPIO_Pin_15; } // 0=off __INLINE void TestPinToggle(void){ GPIOD->ODR ^= GPIO_Pin_15; }
|
|
|
|
|
Jul 24 2012, 19:57
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(demiurg_spb @ Jul 24 2012, 22:05)  Тот кто привык щи лаптем хлебать того уже более прогрессивным прибором не заманишь к столу. Без обид... Да какие обиды... Вопрос в их прогрессивности. Чем такой варинт хуже Код __INLINE void TestPinOn(void) { GPIOD->BSRRL = GPIO_Pin_15; } ? Тем более при усложнившейся структуре портов. Не усложнятся ли макросы до полной непонятности? Получится тоже что "писать в регистры" vs "использовать библиотеку". Цитата(demiurg_spb @ Jul 24 2012, 22:05)  Вы внимательнее ознакомьтесь с этими макросами и возможно осознаете всю их прелесть. Всё возможно... Цитата(demiurg_spb @ Jul 24 2012, 22:05)  Конечно их можно и нужно заменить на шаблоны при переходе на с++ Теоритически можно, и наверное нужно. Но как-то руки не доходят.
|
|
|
|
|
Jul 25 2012, 05:24
|
Профессионал
    
Группа: Свой
Сообщений: 1 047
Регистрация: 28-06-07
Из: Israel
Пользователь №: 28 763

|
Цитата(_Артём_ @ Jul 24 2012, 21:57)  Да какие обиды... Вопрос в их прогрессивности. Чем такой варинт хуже Код __INLINE void TestPinOn(void) { GPIOD->BSRRL = GPIO_Pin_15; } ? ДА ничем. ТЕм более, что я показал самый простой вариант. В общем виде порт и пин задаются дефайнами а не в явном виде. А при наличии __INLINE это считай тот-же макрос.
|
|
|
|
|
Jul 25 2012, 05:47
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(_Артём_ @ Jul 24 2012, 23:57)  Чем такой варинт хуже Код __INLINE void TestPinOn(void) { GPIOD->BSRRL = GPIO_Pin_15; } ? этот теоретически ничем, а вот с инверсией INLINE процедура содержит реальную багу (не обеспечивает атомарного доступа) и при условии что в прерываниях происходит запись в тот же порт будут танцы с бубном. Чтобы пофиксить это нужно работать через bitband или переписать как Код if (pin==1) pin=0; else pin=1; Поищите по форуму, это уже обсуждалось не один раз. Цитата Тем более при усложнившейся структуре портов. Не усложнятся ли макросы до полной непонятности? Получится тоже что "писать в регистры" vs "использовать библиотеку". Нет ничего не усложнится. Суть этих макросов в повторном использовании кода с целью сделать проект прозрачным, переносимым и легко модифицируемым. В случае не использования макросов вам придётся писать для каждого пина снова и снова по 3-4 инлайн процедуры, потом при переразводке платы снова править в 3-4 местах и не дай Бог где-то что-то упустить. Неужели это радостная и продуктивная работа? Цитата(Allregia @ Jul 25 2012, 09:24)  В общем виде порт и пин задаются дефайнами а не в явном виде. А потом вдруг требуется инвертировать один из сигналов по какой-либо причине. И? Сели в лужу?
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Jul 25 2012, 07:18
|
Профессионал
    
Группа: Свой
Сообщений: 1 047
Регистрация: 28-06-07
Из: Israel
Пользователь №: 28 763

|
Цитата(demiurg_spb @ Jul 25 2012, 07:47)  А потом вдруг требуется инвертировать один из сигналов по какой-либо причине. И? Сели в лужу? Ну так я потрачу 5 минут своего драгоценного времени, на то чтобы поменять в одном месте местами BSRRL и BSRRH, только и всего. Все равно 99.999% времени занимает основная задача, а не настройка портов и т.п. Я даже на PIC16 таким не заморачивался, хотя понятное дело что сложность решаемых задач там пониже. Помнить все флаги и биты регистров это конечно хорошо, но я предпочитаю их посмотреть в букваре когда пишу настройки и работу с периферией, а после этого сразу поскорее забыть
|
|
|
|
|
Jul 25 2012, 11:48
|
Профессионал
    
Группа: Свой
Сообщений: 1 047
Регистрация: 28-06-07
Из: Israel
Пользователь №: 28 763

|
Цитата(ViKo @ Jul 25 2012, 09:30)  Но, видимо, на регистры и биты ADC, DMA у вас аллергия? Такой стиль называется "эклектика". У меня не аллергия, но делая прибор на процессорах типа Кортексов, я хочу сосредатачиваться на основной задаче, а не на побочных, типа настойки периферии. Т.е. если у меня идет прием ЭКГ (электрокардиограммы) с АЦП, обработка и выдача результатов на дисплей и в УАРТ, то 99.99% времени я предпочитаю тратить не на настройку АЦП, дисплея и УАРТа, и копание в их битах, а на прикладные вопросы, связанные с ЭКГ и ее обработкой и отображением. Что поверьте мне, гораздо более трудоемко и наукоемко. (тем более, что кроме программы я еще и все железо делаю).
|
|
|
|
|
Jul 25 2012, 11:57
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(Allregia @ Jul 25 2012, 14:48)  99.99% времени я предпочитаю тратить не на настройку АЦП, дисплея и УАРТа, и копание в их битах, а на прикладные вопросы, связанные с ЭКГ и ее обработкой и отображением. Что поверьте мне, гораздо более трудоемко и наукоемко. Верю. Достойное дело. Но если использовать библиотечные функции для работы (не инициализации) с периферией, то можно понапрасну растерять производительность микроконтроллера, так, что на фильтрацию и отображение уже и не хватит.
|
|
|
|
|
Jul 25 2012, 12:50
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(kan35 @ Jul 25 2012, 15:26)  никто не отрицает, что работа через библиотеки менее эффективна, чем прямая работа с регистрами - это очевидно и ни для кого не секрет. Но следует здраво оценивать баланс между потерями и преимуществами. Наверное в 99% задач пофиг - как будет управляться периферия. Возможно, у некоторых (может быть у вас, кстати), задач критичных к этому будет 100%, а у некоторых как у меня - 0% и таких как я - большинство я уверен :-) . Для абсолютных противников библиотек они все же могут быть полезны как учебник по работе с периферией. Из этого я делаю вывод, что библиотеки - это хорошо. Еще недавно вы утверждали, что это использование библиотек - это абсолютно правильный путь. Надо думать, что сейчас у вас уже нет такой 100% уверенности?  Я не считаю себя противником того, что кто-то сделал до меня. Наоборот, всегда полезно посмотреть. Например, когда подберусь к USB, буду смотреть и в библиотеку. Но, например, когда хотел разобраться с SPI, быстро задвинул библиотеку подальше. Разбираться с ней мне показалось бессмысленной тратой времени. Невозможно понять работу железа, пользуясь готовыми функциями. Думаю, то же произойдет и с USB. Мне казалось, что на 72 MHz все будет летать. Ан нет...
|
|
|
|
|
Jul 25 2012, 14:46
|
Профессионал
    
Группа: Свой
Сообщений: 1 047
Регистрация: 28-06-07
Из: Israel
Пользователь №: 28 763

|
Цитата(ViKo @ Jul 25 2012, 13:57)  Верю. Достойное дело. Но если использовать библиотечные функции для работы (не инициализации) с периферией, то можно понапрасну растерять производительность микроконтроллера, так, что на фильтрацию и отображение уже и не хватит. Можно. Но можно также голову иметь, и понимать что когда можно, а что когда нельзя (я ведь выше писал про time critical места). И смею Вас уверить, за 25+ лет общения с разными МК, я этому чуть-чуть научился. Цитата Но, например, когда хотел разобраться с SPI, быстро задвинул библиотеку подальше. Разбираться с ней мне показалось бессмысленной тратой времени. И совершенно напрасно. К примеру, в одном варианете этого устройства у меня дисплей на SPI сидит (в другом - на FSMC), у меня нет ни малейшего желания разбиратсья как там вычисляются коэффициенты делителей для SPI и UARTов, поэтому мне проще написать Baud=115200; и Init() чем самому соображать. И для этого библиотеки вполне пригоды. А про realtime работу - никто не мешает и напрямую с флагами/регистрами, см. выше. Цитата Невозможно понять работу железа, пользуясь готовыми функциями. Думаю, то же произойдет и с USB. Чтобы понять как железо устроено - надо даташит читать, но это вовсе не отменяет пользование бибиотеками. И вот в USB так у меня точно без не то что библиотк, а вообще драйверов, никакого желания ковыряться не возникнет.
|
|
|
|
|
Jul 26 2012, 05:52
|
Знающий
   
Группа: Участник
Сообщений: 537
Регистрация: 22-02-06
Пользователь №: 14 594

|
Цитата(ViKo @ Jul 25 2012, 16:50)  Еще недавно вы утверждали, что это использование библиотек - это абсолютно правильный путь. Надо думать, что сейчас у вас уже нет такой 100% уверенности?  Я не считаю себя противником того, что кто-то сделал до меня. Наоборот, всегда полезно посмотреть. Например, когда подберусь к USB, буду смотреть и в библиотеку. Но, например, когда хотел разобраться с SPI, быстро задвинул библиотеку подальше. Разбираться с ней мне показалось бессмысленной тратой времени. Невозможно понять работу железа, пользуясь готовыми функциями. Думаю, то же произойдет и с USB. Мне казалось, что на 72 MHz все будет летать. Ан нет...  Viko, вы любите передергивать  Если у вас "не летает" на 72 МГц, то стоит задуматься над тем как вы работаете с периферией. Как правило упомянутая вами работа с битами-флагами это подход который стоит применять в исключительных случаях. Вся основная работа должна происходить по DMA. У меня напрмер прекрасно уживаются mp3 плеер с tcp/ip, usb-msd и пользовательским интерфейсом и при этом проц в самом худшем случае 50% времени бездельничает. Я пользуюсь периферийной библиотекой (в том числе usb от st) :-) Но если тот же SPI или ADC обслуживать поллингом битов, то никакого быстродействия не хватит (даже напрямую обращаясь к регистрам), не говоря уже о том, что нужно данные готовить или обрабатывать.
Сообщение отредактировал kan35 - Jul 26 2012, 05:55
|
|
|
|
|
Jul 26 2012, 07:58
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(kan35 @ Jul 26 2012, 08:52)  Viko, вы любите передергивать  Да ну. Так, по мелочам придирки. Шутки. Цитата Вся основная работа должна происходить по DMA. Вот вам мой пример. Пересылаю из внешней ОЗУ в контроллер ЖКИ. По ПДП оказалось медленнее, чем программно. Код void DpyBuf2LCD_copy(uint32_t Offset, uint32_t Size) { /* LED_on(); DMA2->IFCR |= DMA_IFCR_CTCIF1; // сбросить флаг прерывания DMA2_Channel1->CCR &= ~0x00000001; // запретить пересылку DMA2_Channel1->CPAR = DPYBUF + Offset; // начальный адрес буфера экрана DMA2_Channel1->CMAR = LCDRAM + Offset; // начальный адрес памяти ЖКИ DMA2_Channel1->CNDTR = Size/2; // 16-битовые пересылки DMA2_Channel1->CCR |= 0x00000001; // разрешить пересылку // while (!(DMA2->ISR & DMA_ISR_TCIF1)); // ждать флаг прерывания LED_off(); */ /* // Программная пересылка буфера в контроллер ЖКИ uint16_t *pSour = (uint16_t *)(DPYBUF + Offset); uint16_t *pDist = (uint16_t *)(LCDRAM + Offset); LED_on(); for (uint32_t i=Size/2; i--; ) { *pDist++ = *pSour++; } LED_off(); */
// Программная пересылка буфера в контроллер ЖКИ 32-битовыми словами uint32_t *pSour = (uint32_t *)(DPYBUF + Offset); uint32_t *pDist = (uint32_t *)(LCDRAM + Offset); // LED_on(); for (uint32_t i=Size/4; i--; ) { *pDist++ = *pSour++; } // LED_off(); }
|
|
|
|
|
Jul 26 2012, 08:36
|
Знающий
   
Группа: Участник
Сообщений: 537
Регистрация: 22-02-06
Пользователь №: 14 594

|
То, что по DMA работа (пусть даже внешняя память--внешняя память) оказалась медленнее - сюрприз. Ваши версии - почему этот нелогичный эффект получился, может быть DMA перегружен другой задачей?
Мы немного отклонились от темы, но у раз речь зашла, из своей практики скажу. В свое время делал драйвер для LCD контроллера на SPI, так благодаря DMA получилось даже сделать аналог графического ускорителя - рисование гор/верт линий было аппаратным, что очень ускорило работу того же ucGUI 320*240*16бит на SPI. Когда патался оптимизировать программный способ передачи, то работало в 2-4 раза медленнее, не говоря о том, что CPU только дисплеем и занимался.
|
|
|
|
|
Jul 26 2012, 08:45
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(kan35 @ Jul 26 2012, 11:36)  То, что по DMA работа (пусть даже внешняя память--внешняя память) оказалась медленнее - сюрприз. Ваши версии - почему этот нелогичный эффект получился, может быть DMA перегружен другой задачей? Все связано с внутренними шинами микроконтроллера. В данном примере был STM32F103. Окончание пересылки проверялось программно. Таким образом, микроконтроллер пытался параллельно выполнять код и пересылать из памяти в память. Еще отладчик Keil uLink-ME висел, он тоже что-то забирал. Ну, и программные пересылки я сделал 32-битовыми словами. Не помню, можно ли такое сделать по ПДП. P.S. В STM32F2, F4 есть Bus matrix, там таких тормозов уже не будет, наверное.
|
|
|
|
|
Jul 26 2012, 20:29
|
Профессионал
    
Группа: Свой
Сообщений: 1 047
Регистрация: 28-06-07
Из: Israel
Пользователь №: 28 763

|
Вопрос немного в сторону, про М4 и настройки Кейла. Вот окно настроек:
У меня как-то раньше на LPC17xx небыло нужды туда лазить, оставлял все по дефолту. Но тут во первых наконец хочеться разобраться что к чему, во вторых есть вопрос по F4. Слепа ПЗУ, справа ОЗУ. И там есть ROM*/IROM* и RAM*/IRAM*. Насколько я понял. ROM* и RAM* это если цепляются внешние на FSCM, а пурвая буква "i" означает "internal", т.е то что внутри проца. Разбивать IROM на 2 части может понадобится для собственного бутлоадера а таже для использования части флеша как ЕЕПРОМ. Радиобаттоны возле ROM* определяют откуда стартовать. А вот что делает чексбокс default? Далее - с RAM. Тут тоже можно разбить на 2 области, но для к примеру LPC17хх или для F1 не обчень понятнодля чего это надо. И для чего нужны чекбоксы "No Init" рядом с ними? В Кейле нет возможности сказать про какую-то переменную, что ее не надо инициализировать при старте (аналог persistant в Hi-Tech)? А главное - у F4 две области памяти, общая 128к плюс 64к только для CPU. Keil по умолчанию ставить первую, а вторую не использует. По идее, правильнее всего было бы размещать в первой только е переменные, которым нужно DMA, а те, к которым обращается только процессор - во второй. Но как распределять иак переменые и как обьяснить это линкеру?
|
|
|
|
|
Nov 19 2012, 15:28
|
Местный
  
Группа: Свой
Сообщений: 252
Регистрация: 9-10-08
Из: Московская обл.
Пользователь №: 40 797

|
Перепишите на обычные (regular) каналы, injected не могут работать в непрерывном режиме сами по себе. Цитата Note: Injected channels cannot be converted continuously. The only exception is when an injected channel is configured to be converted automatically after regular channels in continuous mode (using JAUTO bit), refer to Auto-injection section).
|
|
|
|
|
Jan 9 2014, 08:17
|
Частый гость
 
Группа: Участник
Сообщений: 90
Регистрация: 12-12-13
Пользователь №: 79 587

|
Необходимо измерить сигнал с нескольких каналов последовательно, сохраняя данные в память через DMA. Измерять надо крайне быстро. Действую таким образом: выбираю канал, запускаю АЦП. Вот кусок кода: Код if(ModeStruct.Discreteness_hall) { if ((step_counter % ModeStruct.Discreteness_hall) == 0) { ADC1->SQR3 = ADC_Channel_0; ADC1->CR2 |= ADC_CR2_SWSTART; //Start ADC } } if (ModeStruct.Discreteness_l) { if ((step_counter % ModeStruct.Discreteness_l) == 0) { ADC1->SQR3 = ADC_Channel_1; ADC1->CR2 |= ADC_CR2_SWSTART; //Start ADC } } if (ModeStruct.Discreteness_diff) { if ((step_counter % ModeStruct.Discreteness_diff) == 0) { ADC1->SQR3 = ADC_Channel_2; ADC1->CR2 |= ADC_CR2_SWSTART; //Start ADC } } Вот инициализация (тоже кусок): Код ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b; ADC_InitStruct.ADC_ScanConvMode = DISABLE; ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_NbrOfConversion = 1; ADC_Init(ADC1, &ADC_InitStruct); Внимание, вопрос. Если я использую только один АЦП, нужна ли инициализация ADC_CommonInit(&ADC_CommonInitStruct)? Какая частота тактирования у АЦП? В заблуждение ввел предделитель в вышеупомянутой ADC_CommonInitStruct. Если я работаю только с одним АЦП, его тактовая частота будет равна частоте шины APB2 без всяких предделителей? Если чатстота APB2 = 84 МГц, а частота ядра 168Мгц, то в моем случае на оцифровку одного канала будет 15*(186/84) = 15*2 = 30 системных тиков. Верно? Если да, то поидее должен успеть оцифровать до запуска следующего канала. Но для подстраховки, перед запуском очередного преобразования, как лучше проверить (поять же, максимально быстро) завершилось ли предыдущее преобразование? Пробовал перед началом преобразования проверять флаг конца преобразования (EOC), но он похоже сбрасывается, когда DMA забирает данные в память.
Сообщение отредактировал Haamu - Jan 9 2014, 08:28
|
|
|
|
|
  |
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|