Uzix | микроблог

Лямбды в Си

Есть два пути получения лямбд в Си:

  1. Использовать дополнительный препроцессор cmacro. Идея очень интересная, но, к сожалению, реализация подводит. У меня этот препроцессор не только спотыкался на простейших конструкциях (например, лямбда, возвращающая указатель), но и умудрялся портить совершенно сторонний код (“return -1” чудесным образом превращается в “return- 1”). Проект по всей видимости заброшен.

  2. Использовать расширения компилятора GCC. Это простой и безболезненный путь если нет необходимости сохранять совместимость с другими компиляторами. Для этого варианта достаточно объявить макрос:

#define lambda(l_ret_type, l_arguments, l_body)         \
  ({                                                    \
    l_ret_type l_anonymous_functions_name l_arguments   \
      l_body                                            \
    &l_anonymous_functions_name;                        \
  })

Пример создания и использования лямбды:

    printf("%s world\n", lambda(char*, (void), { return "Hello"; } )() );

Обратите внимание: GCC волен размещать код вложенных функций (и, как следствие, лямбд) в стеке!

Анализ сбоев МК пост-фактум

Часто возникает необходимость зафиксировать состояние МК во время сбоя (деление на 0, разыменование нулевого указателя, обращение к невыровненному адресу и тысяча других возможностей прострелить себе ногу). И часто при этом нет возможности держать устройство с подключенным отладчиком.

В таких случаев поможет связка ПО CrashDebug и CrashCatcher. CrashCatcher - библиотека подключающаяся к прошивке МК. Она берёт на себя обработку исключения HardFault и вывод содержимого регистров и памяти на последовательный порт (либо любым другим реализованным программистом способом). Полученный по последовательному порту дамп сохраняется в файл и передаётся утилите CrashDebug, выступающей в роли target для GDB. Дальше уже можно работать любыми привычными средствами - консольным arm-none-eabi-gdb, ddd, либо даже Eclipse (через тип конфигурации “GDB Hardware Debug”).

Оба проекта имеют исчерпывающую документацию и достаточно легко интегрируются в существующий проект.

Если вы калибруете АЦП...

… то не забывайте перед калибровкой отключать DMA (ADC1->CFGR1 &= ~ADC_CFGR1_DMAEN) и сбрасывать значение счётчика DMA1_ChannelN->CNDTR.

static void adc_recalibrate( void )
{
    ADC1->CR |= ADC_CR_ADSTP;                                                       // Stop current conversion
    while( ADC1->CR & ADC_CR_ADSTP );                                               // .. and wait
    ADC1->CFGR1 &= ~ADC_CFGR1_DMAEN;                                                // Disable DMA req
    ADC1->CR |= ADC_CR_ADDIS;                                                       // Disable ADC
    while( ADC1->CR & ADC_CR_ADEN );                                                // .. and wait
    DMA1_Channel1->CCR &= ~DMA_CCR_EN;                                              // Stop DMA transfer
    while( DMA1_Channel1->CCR & DMA_CCR_EN );                                       // .. and wait
    ADC1->CR |= ADC_CR_ADCAL;                                                       // Start ADC calibration
    while( ADC1->CR & ADC_CR_ADCAL );                                               // .. and wait
    ADC1->CR |= ADC_CR_ADEN;                                                        // Enable ADC
    while( !(ADC1->ISR & ADC_ISR_ADRDY) );                                          // .. and wait
    ADC1->CFGR1 |= ADC_CFGR1_DMAEN;                                                 // Enable DMA req
    ADC1->CR |= ADC_CR_ADSTART;                                                     // Start ADC conversion
    DMA1_Channel1->CNDTR = ADC_CHAN_COUNT * ADC_FILTER_SIZE;                        // Set cycle size
    DMA1_Channel1->CCR |= DMA_CCR_EN;                                               // Start DMA transfer
}

Пример кода для STM32F030C8

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

Такие дела.

Отладка RTOS в OpenOCD

Для отладки RTOS помимо плагинов IDE можно использовать встроенные средства OpenOCD. На данный момент поддерживаются FreeRTOS, ChibiOS, eCos и другие.

Для использования этой функциональности нужно в параметры запуска openocd добавить следующую строку (для openocd 0.9 и stm32f4):

-c "stm32f4x.cpu configure -rtos auto"

В случае FreeRTOS версий старше 7.5.3 нужно выполнить дополнительные движения:

  • Добавить в исходный код определение:
const volatile int __attribute__((used)) uxTopUsedPriority = configMAX_PRIORITIES;
  • Добавить в параметры запуска компоновщика:
-Wl,--undefined=uxTopUsedPriority

Настройка Eclipse для разработки под STM32

Инструкций сходной тематики в интернете немало, но как то вышло что информацию мне всё равно пришлось собирать по кусочкам. В этой статье я подытожу весь процесс от А до Я. Всё будет делаться на примере дистрибутива Debian Sid и отладочной платы STM32F4Discovery.

Читать далее →