За последние 10 лет я поменял 3 работы, прособеседовался с 10+ компаний на позицию разработчика (software engineer) и вел переписку с HR/рекрутерами из нескольких десятков фирм. По ходу дела заметил, что вопросы, которые я задаю на собеседовании с менеджером/командой или с HR, повторяются, и решил их структурировать. Некоторые из них являются общими, и их может задать кандидат на почти любую вакансию; другие касаются только вакансий для программистов. В этой статье поделюсь с вами наиболее типичными и важными вопросами, которые, на мой взгляд, может задать соискатель потенциальному работодателю.
Пользователь
Перенаправляем printf() из STM32 в консоль Qt Creator
Нередко при отладке ПО микроконтроллера возникает необходимость вывода отладочных сообщений, логов, захваченных данных и прочего на экран ПК. При этом хочется, чтобы и вывод был побыстрее, и чтобы строки отображались не где-нибудь, а прямо в IDE — не отходя от кода, так сказать. Собственно, об этом и статья — как я пытался printf() выводить и отображать внутри любимой, но не очень микроконтроллерной, среды Qt Creator.
Дебаггинг в реальном времени через JTAG/SWJ-DP для микроконтроллеров на ядре ARM Cortex-M
![](https://habrastorage.org/files/162/860/2b6/1628602b65a44f9180bfa7181caa6398.png)
С некоторых пор фирма Segger предлагает технологию Real Time Terminal (RTT) для своих JTAG адаптеров J-Link. Суть ее в том, что программа на микроконтроллере может выводить и принимать отладочную информацию из JTAG/SWJ-DP порта, как это обычно делается через UART. И тогда нам больше не нужен реальный отладочный UART. Далее чуть подробнее о возможностях этой технологии.
STM32F103C8T6 как накопитель flash с файловой системой FAT12
Рассмотрим вариант хранения в пожалуй самых распространенных микроконтроллерах STM серии F103. Способствовала распространенности также всем известная макетная плата Blue Pill
![image](https://habrastorage.org/webt/59/of/v3/59ofv3if6laponckzryro9ojnqa.jpeg)
Имеющаяся в ней flash позволяет не только хранить и модифицировать настройки используя файловую систему FAT12 во внутреннем flash, но и организовать обновление прошивки.
Согласно документации в STM32F103C8T6 имеется 64К flash памяти. Однако практически во всех STM32F103C8T6 установлено 128К. Об этом также упоминается в разных источниках — обычно ставят на 64К больше. Такая «фича» позволяет использовать микроконтроллер как flash накопитель объемом 128К — 20К (системные нужды FAT12) — размер прошивки.
Многие энтузиасты, пытавшиеся использовать данный контроллер как накопитель flash, сталкивались с проблемой его использования в режиме файловой системы FAT12. Использовать для снятия/заливки образа диска получалось. А вот при работе как с файловым накопителем начинались проблемы.
STM32 USB Mass Storage Bootloader
Загрузчик — это удобно и полезно, не правда ли? А если загрузчик собственной реализации, то это еще более удобно, полезно и гибко
Так же, это прекрасная возможность углубиться и изучить особенности используемой вычислительной машины — в нашем случае микроконтроллера STM32 с ядром ARM Cortex-M3.
На самом деле, загрузчик — это проще, чем кажется на первый взгляд. В доказательство, под cut'ом соберём свой собственный USB Mass Storage Bootloader!
![image](https://habrastorage.org/files/cf8/56c/76a/cf856c76a8e5427fb353114dd2a01566.jpg)
Запускаем мелкосерийное производство электроники. Личный опыт
![](https://habrastorage.org/r/w780/getpro/habr/upload_files/597/b38/675/597b38675941df70ccb0619f0c01a5a6.jpeg)
В любой компании, занимающейся разработкой электроники в России регулярно поднимаются два вопроса, которые приводят к жарким холиварам: как паять прототипы и где запускать серийное производство. Ответ на каждый вопрос, по сути, сводится к выбору между аутсорсом либо производству собственными силами. В статье описывается личный опыт обустройства лаборатории для прототипирования единичных экземпляров и мелкосерийного производства электроники собственной разработки. Возможно, кому то он окажется полезен при обустройстве собственного свечного заводика.
Чтобы статья получилась максимально практичной в ней будут приводиться ссылки на поставщиков оборудования, которое используется нами. Не сочтите за рекламу.
Мой топ IT книг из прошлого века, актуальных до сих пор
В этой статье автор предложил написать свои книги, которые относятся к разряду неувядающей классики. Если в оригинальной статье был сделан упор на электронику, то у меня будет упор на разработку программ.
Disclaimer: это мой личный топ из тех книг, которые я лично прочитал, и у которых первое издание было в прошлом веке, даже если она переиздавалась недавно (при условии актуальности именно того издания, которое было в прошлом веке).
В данном топе книги не упорядочены по важности, они все очень хорошие, но есть одна книга, которая равнее других.
Локальный запуск юнит-тестов в STM32CubeIDE под Windows
Введение
Всем известна польза юнит-тестирования. Прежде всего, написание тестов одновременно с кодом позволяет раньше выявлять ошибки и не тратить впоследствии время на трудоемкую комплексную отладку. В случае embedded-разработки у юнит-тестирования есть особенности, связанные, во-первых, с тем, что код выполняется где-то глубоко в недрах устройства и взаимодействовать с ним довольно сложно, и, во-вторых, код сильно завязан на целевое железо.
Если в проекте есть фрагменты, не зависящие от аппаратуры и при этом реализующие достаточно сложную логику, для них применение модульных тестов даст наибольшую выгоду. Например, это может быть реализация какого-то протокола передачи данных, различные расчеты или управляющий конечный автомат.
Существует три способа запуска юнит-тестов для встраиваемых платформ:
Топ-10 книг для разработчика
Совершенствоваться в сфере разработки — это не только писать хороший код, но и читать о том, как его писать.
![](https://habrastorage.org/webt/ar/nr/_w/arnr_wa-tnbf2jdpzutvjew3wfa.jpeg)
Привет, хабр! Продолжаю делиться полезными подборками. Совсем недавно я опубликовал 2 поста с перечнем Github репозиториев: Часть1 и Часть2. На этот раз предлагаю вашему вниманию подборку полезных книг для разработчиков. Кому интересно — добро пожаловать под кат.
CUnit: Автоматическое тестирование с динамической загрузкой тестов
Здесь я не буду описывать как работает CUnit или как писать тест-кейсы и тест-сьюты с использованием данного фреймворка. Все это есть в официальной документации, которая расположена по адресу http://cunit.sourceforge.net/doc/index.html.
Авто-регистрация тестов на С средствами языка
![Тестирование в C](https://habrastorage.org/files/3d3/aed/597/3d3aed597f8645ed9d1502e4532642ee.png)
Полуавтоматическая регистрация юнит-тестов на чистом С
Совсем недавно я узнал о минималистичном фреймворке minUnit, который умещается всего в 4 строчки.
Ещё один способ автоматического вызова unit-тестов на языке Си
![](https://habrastorage.org/files/ec6/584/f50/ec6584f5014b4e4d901e14c70090a604.png)
Кому интересно, прошу подкат! Но предупреждаю: там много портянок из макросов и «линкерской» магии.
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 4 (Прерывания, UART и недоHART)
Ведение
Попав в отпуске в город на Неве и посетив множество красивых мест, я все таки, вечерами за чашкой пива, разбирался с UARTом. Тем более, что я купил неплохие наушники Fisher FA011, к которым пришлось прикупить USB SOUND BLASTER X-FI HD и хотел послушать музыку.
Предыдущие статьи вначале переехали на Geektime потом я обратно их перегнал, даже и не знаю, куда теперь их деть :)
Но так на всякий случай они тут:
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 1
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 2 и
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 3 (LCD и Экраны)
UART
После детального изучения микроконтроллера, мне казалось, что все просто. Настройка и тестовая посылка байта в порт прошла без задоринки, все работало как часы, и тут я решил использовать прерывания. Нужно было сделать так, чтобы обработчик прерывания был статическим методом класса. И IAR в руководстве на компилятор, так и писал:
Special function types can be used for static member functions. For example, in the
following example, the function handler is declared as an interrupt function:
class Device { static __irq void handler(); };
Но вот незадача, для Cortex M такой способ не подходит и
On ARM Cortex-M, an interrupt service routine enters and returns in the same way as a
normal function, which means no special keywords are required. Thus, the keywords
__irq, __fiq, and __nested are not available when you compile for ARM Cortex-M.
These exception function names are defined in cstartup_M.c and cstartup_M.s.
They are referred to by the library exception vector code:
NMI_Handler
HardFault_Handler
MemManage_Handler
BusFault_Handler
…
The vector table is implemented as an array. It should always have the name
__vector_table,
Или по простому, ваш обработчик прерывания должен иметь такое же имя, какое он имеет в таблице векторов определенной в startup файле. Это делается с помощью специального ключевого слова — слабой ссылки __weak (в ассемблере PUBWEAK), которая означает, что данное определение будет использоваться до тех пора, пока не найдется хотя бы одно совпадающее по написанию без ключевого слова __week. Ну т.е., если вы определите функцию с точно таким же именем без этой директивы, то компилятро будет использовать это определение, а если не определите, то которое помечено __weak.
Понятное дело, что я не могу в файл startup_stm32l1xx_md.s или startup_stm32l1xx_md.с вставить С++ имя статического метода типа cUart::USART2_IRQHandler(), ассемблер его просто не поймет.
А просто «USART2_IRQHandler» не совпадает с определением «cUart::USART2_IRQHandler()».
Можно использовать extern «C» { void USART2_IRQHandler(void) {...}}, но это означает, что я тут буду делать вставки из Си, что мне совсем не надо, и вообще доступа из такой функции к атрибутам моего класса, например буферу — не будет, и надо будет городить кучу некрасивого кода :).
Поэтому, я решил пойти другим путем и создать файл startup_stm32l1xx_md.cpp. Поиск в интернете обнаружил, что точно такая же проблема была у некоторых людей Вот например
В общем идея заключается в следующем: Объявляем в startup_stm32l1xx_md.cpp классы со статическими методами (которые и будут являться обработчиками прерываний), создаем таблицу __vector_table, где на каждом из векторов прерываний стоит указатель на эти статические методы. Дальше делаем __weak определение каждого метода
И теперь когда в коде компилятор видет реализацию void cUart1::handler(), он не задумываясь берет её. Конечно же при этом ваши классы и методы должны называться точь в точь так, как они определены в startup_stm32l1xx_md.cpp.
Нужно еще не забыть про функции FreeRtos: vPortSVCHandler, xPortPendSVHandler, xPortSysTickHandler и поставить их на нужное прерывание и вуаля — все работает:
#pragma language = extended
#pragma segment = "CSTACK"
extern "C" void __iar_program_start( void );
extern "C" void vPortSVCHandler(void);
extern "C" void xPortPendSVHandler(void);
extern "C" void xPortSysTickHandler(void);
class cNMI
{
public:
static void handler(void);
};
class cHardFault
{
public:
static void handler(void);
};
class cMemManage
{
public:
static void handler(void);
};
class cBusFault
{
public:
static void handler(void);
};
class cUsageFault
{
public:
static void handler(void);
};
class cDebugMon
{
public:
static void handler(void);
};
class cWindowWatchdog
{
public:
static void handler(void);
};
class cPvd
{
public:
static void handler(void);
};
class cTamperTimeStamp
{
public:
static void handler(void);
};
class cRtcWakeup
{
public:
static void handler(void);
};
class cFlash
{
public:
static void handler(void);
};
class cRcc
{
public:
static void handler(void);
};
class cExti
{
public:
static void line0Handler(void);
static void line1Handler(void);
static void line2Handler(void);
static void line3Handler(void);
static void line4Handler(void);
static void line9Handler(void);
static void line15_10Handler(void);
};
class cDma
{
public:
static void channellHandler(void);
static void channel2Handler(void);
static void channel3Handler(void);
static void channel4Handler(void);
static void channel5Handler(void);
static void channel6Handler(void);
static void channel7Handler(void);
};
class cAdc
{
public:
static void handler(void);
};
class cDac
{
public:
static void handler(void);
};
class cUsb
{
public:
static void highPriorityHandler(void);
static void lowPriorityHandler(void);
static void fsWakeupHandler(void);
};
class cComp
{
public:
static void handler(void);
};
class cLcdDriver
{
public:
static void handler(void);
};
class cTim9
{
public:
static void handler(void);
};
class cTim2
{
public:
static void handler(void);
};
class cTim3
{
public:
static void handler(void);
};
class cTim4
{
public:
static void handler(void);
};
class cTim10
{
public:
static void handler(void);
};
class cTim6
{
public:
static void handler(void);
};
class cTim7
{
public:
static void handler(void);
};
class cTim11
{
public:
static void handler(void);
};
class cI2C1
{
public:
static void eventHandler(void);
static void errorHandler(void);
};
class cI2C2
{
public:
static void eventHandler(void);
static void errorHandler(void);
};
class cSpi1
{
public:
static void handler(void);
};
class cSpi2
{
public:
static void handler(void);
};
class cUart1
{
public:
static void handler(void);
};
class cUart2
{
public:
static void handler(void);
};
class cUart3
{
public:
static void handler(void);
};
class cRtcAlarm
{
public:
static void handler(void);
};
typedef void( *intfunc )( void );
typedef union { intfunc __fun; void * __ptr; } intvec_elem;
// The vector table is normally located at address 0.
// When debugging in RAM, it can be located in RAM, aligned to at least 2^6.
// If you need to define interrupt service routines,
// make a copy of this file and include it in your project.
// The name "__vector_table" has special meaning for C-SPY:
// it is where the SP start value is found, and the NVIC vector
// table register (VTOR) is initialized to this address if != 0.
#pragma location = ".intvec"
extern "C" const intvec_elem __vector_table[] =
{
{ .__ptr = __sfe( "CSTACK" ) },
__iar_program_start,
cNMI::handler,
cHardFault::handler,
cMemManage::handler,
cBusFault::handler,
cUsageFault::handler,
0,
0,
0,
0,
vPortSVCHandler, //функции freeRTOS не трогать!
cDebugMon::handler,
0,
xPortPendSVHandler, //функции freeRTOS не трогать!
xPortSysTickHandler, //функции freeRTOS не трогать!
//External Interrupts
cWindowWatchdog::handler, //Window Watchdog
cPvd::handler, //PVD through EXTI Line detect
cTamperTimeStamp::handler, //Tamper and Time Stamp
cRtcWakeup::handler, //RTC Wakeup
cFlash::handler, //FLASH
cRcc::handler, //RCC
cExti::line0Handler, //EXTI Line 0
cExti::line1Handler, //EXTI Line 1
cExti::line2Handler, //EXTI Line 2
cExti::line3Handler, //EXTI Line 3
cExti::line4Handler, //EXTI Line 4
cDma::channellHandler, //DMA1 Channel 1
cDma::channel2Handler, //DMA1 Channel 2
cDma::channel3Handler, //DMA1 Channel 3
cDma::channel4Handler, //DMA1 Channel 4
cDma::channel5Handler, //DMA1 Channel 5
cDma::channel6Handler, //DMA1 Channel 6
cDma::channel7Handler, //DMA1 Channel 7
cAdc::handler, //ADC1
cUsb::highPriorityHandler, //USB High Priority
cUsb::lowPriorityHandler, //USB Low Priority
cDac::handler, //DAC
cComp::handler, //COMP through EXTI Line
cExti::line9Handler, //EXTI Line 9..5
cLcdDriver::handler, //LCD
cTim9::handler, //TIM9
cTim10::handler, //TIM10
cTim11::handler, //TIM11
cTim2::handler, //TIM2
cTim3::handler, //TIM3
cTim4::handler, //TIM4
cI2C1::eventHandler, //I2C1 Event
cI2C1::errorHandler, //I2C1 Error
cI2C2::eventHandler, //I2C2 Event
cI2C2::errorHandler, //I2C2 Error
cSpi1::handler, //SPI1
cSpi2::handler, //SPI2
cUart1::handler, //USART1
cUart2::handler, //USART2
cUart3::handler, //USART3
cExti::line15_10Handler, //EXTI Line 15..10
cRtcAlarm::handler, //RTC Alarm through EXTI Line
cUsb::fsWakeupHandler, //USB FS Wakeup from suspend
cTim6::handler, //TIM6
cTim7::handler //TIM7
};
__weak void cNMI::handler() { while (1) {} }
__weak void cHardFault::handler() { while (1) {} }
__weak void cMemManage::handler() { while (1) {} }
__weak void cBusFault::handler() { while (1) {} }
__weak void cUsageFault::handler() { while (1) {} }
__weak void cDebugMon::handler() { while (1) {} }
__weak void cWindowWatchdog::handler() { while (1) {} }
__weak void cPvd::handler() { while (1) {} }
__weak void cTamperTimeStamp::handler() { while (1) {} }
__weak void cRtcWakeup::handler() { while (1) {} }
__weak void cFlash::handler() { while (1) {} }
__weak void cRcc::handler() { while (1) {} }
__weak void cExti::line0Handler() { while (1) {} }
__weak void cExti::line1Handler() { while (1) {} }
__weak void cExti::line2Handler() { while (1) {} }
__weak void cExti::line3Handler() { while (1) {} }
__weak void cExti::line4Handler() { while (1) {} }
__weak void cExti::line9Handler() { while (1) {} }
__weak void cExti::line15_10Handler() { while (1) {} }
__weak void cDma::channellHandler() { while (1) {} }
__weak void cDma::channel2Handler() { while (1) {} }
__weak void cDma::channel3Handler() { while (1) {} }
__weak void cDma::channel4Handler() { while (1) {} }
__weak void cDma::channel5Handler() { while (1) {} }
__weak void cDma::channel6Handler() { while (1) {} }
__weak void cDma::channel7Handler() { while (1) {} }
__weak void cAdc::handler() { while (1) {} }
__weak void cUsb::fsWakeupHandler() { while (1) {} }
__weak void cUsb::highPriorityHandler() { while (1) {} }
__weak void cUsb::lowPriorityHandler() { while (1) {} }
__weak void cDac::handler() { while (1) {} }
__weak void cComp::handler() { while (1) {} }
__weak void cLcdDriver::handler() { while (1) {} }
__weak void cTim2::handler() { while (1) {} }
__weak void cTim3::handler() { while (1) {} }
__weak void cTim4::handler() { while (1) {} }
__weak void cTim6::handler() { while (1) {} }
__weak void cTim7::handler() { while (1) {} }
__weak void cTim9::handler() { while (1) {} }
__weak void cTim10::handler() { while (1) {} }
__weak void cTim11::handler() { while (1) {} }
__weak void cI2C1::errorHandler() { while (1) {} }
__weak void cI2C1::eventHandler() { while (1) {} }
__weak void cI2C2::errorHandler() { while (1) {} }
__weak void cI2C2::eventHandler() { while (1) {} }
__weak void cSpi1::handler() { while (1) {} }
__weak void cSpi2::handler() { while (1) {} }
__weak void cUart1::handler() { while (1) {} }
__weak void cUart2::handler() { while (1) {} }
__weak void cUart3::handler() { while (1) {} }
__weak void cRtcAlarm::handler() { while (1) {} }
extern "C" void __cmain( void );
extern "C" __weak void __iar_init_core( void );
extern "C" __weak void __iar_init_vfp( void );
#pragma required=__vector_table
void __iar_program_start( void )
{
__iar_init_core();
__iar_init_vfp();
__cmain();
}
![image](https://habrastorage.org/files/90c/88d/ced/90c88dcedc674d00b441b6193653f0e0.png)
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 4 (Прерывания, UART и недоHART)
Ведение
Попав в отпуске в город на Неве и посетив множество красивых мест, я все таки, вечерами за чашкой пива, разбирался с UARTом. Тем более, что я купил неплохие наушники Fisher FA011, к которым пришлось прикупить USB SOUND BLASTER X-FI HD и хотел послушать музыку.
Предыдущие статьи вначале переехали на Geektime потом я обратно их перегнал, даже и не знаю, куда теперь их деть :)
Но так на всякий случай они тут:
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 1
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 2 и
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 3 (LCD и Экраны)
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 3 (LCD и Экраны)
Введение
В двух предыдущих частях STM32, C++ и FreeRTOS. Разработка с нуля. Часть 1 и STM32, C++ и FreeRTOS. Разработка с нуля. Часть 2 мною уже были реализованы требования SR0, SR7, SR4 и SR6. Опять нужно вспомнить, какие вообще требования есть.
SR0: Устройство должно измерять три параметра (иметь три переменных): Температуру микропроцессора, Напряжение VDDA, Напряжение с переменного резистора
SR1: Устройство должно выводить значение этих переменных на индикатор.
SR2: Единицы измерения для Температуры микропроцессора — градусы Цельсия, для остальных параметров — вольты.
SR3: При нажатии на кнопку 1, на индикаторе должен показываться экран со следующей измеряемой переменной,
SR4: При нажатии на кнопку 1 Светодиод 1 должен изменять свое состояние
SR5: При нажатии на кнопку 2, на индикаторе должен поменяться режим отображения переменных с постоянного показывания переменной на последовательное (менять экраны раз в 1.5 секунды) при следующем нажатии с последовательного на постоянное,
SR6: При нажатии на кнопку 2 светодиод 2 должен менять свое состояние.
SR7: Светодиод 3 должен моргать раз в 1 секунду.
Значит остались самые «вкусные» требования связанные c отображением всей измеренной информации на индикаторе: SR1, SR2, SR3, SR5. Ну что же начнем.
Можно ли использовать С++ вместо Си для небольших проектов в микроконтроллерах
С++ обертка для «всех» Операционных Систем Реального Времени для CortexM4
![image](https://habrastorage.org/webt/s0/2o/ve/s02ovegatxm9x39z6c8x_5fydki.png)
Я уже рассказывал о том как можно использовать FreeRtos для проектов, написанных на С++ в статье STM32, C++ и FreeRTOS. Разработка с нуля. Часть 1. С тех пор прошло целых 3 года, я серьезно постарел, потерял кучу нейронных связей, поэтому решил встряхнуть стариной для того, чтобы эти связи восстановить и замахнуться на обертку для «любой» популярной ОСРВ. Это конечно шутка, я намеренно взял «всех» в кавычки, но в каждой шутке есть доля правды.
Унифицированная обработка ошибок (C++ вариант для микроконтроллеров)
std
не совсем подходят для реализации в микроконтроллере. Существуют также некоторые ограничения в использовании динамически выделяемой памяти, RTTI, исключений и так далее. В общем случае, чтобы писать компактный и быстрый код нельзя просто так взять библиотеку std
и начать пользоваться, скажем операторами типа typeid
, потому что необходима поддержка RTTI, а это уже накладные расходы, хоть и не очень большие.Поэтому иногда приходится изобретать велосипеды, чтобы выполнить все эти условия. Таких задач немного, но они есть. В данном посте, хотелось бы рассказать про вроде бы как простую задачку — расширить коды возврата существующих подсистем в ПО для микроконтроллера.
Где хранятся ваши константы на микроконтроллере CortexM (на примере С++ IAR компилятора)
Обычно на этом микроконтроллере в ROM память записывается программа, а в RAM изменяемые данные и очень часто нужно сделать так, чтобы константы лежали в ROM.
В микроконтроллере STM32F411RE, ROM память расположена по адресам с 0x08000000...0x0807FFFF, а RAM с 0x20000000...0x2001FFFF.
И если все настройки линкера правильные, студент рассчитывает, что вот в таком незамысловатом коде его константа лежит в ROM:
class WantToBeInROM
{
private:
int i;
public:
WantToBeInROM(int value): i(value) {}
int Get() const
{
return i;
}
};
const WantToBeInROM myConstInROM(10);
int main()
{
std::cout << &myConstInROM << std::endl ;
}
Вы тоже можете пробовать ответить на вопрос: где лежит константа myConstInROM в ROM или в RAM?
Если вы ответили на этот вопрос, что в ROM, поздравляю вас, на самом деле скорее всего вы не правы, константа в общем случае будет лежать в RAM и чтобы разобраться, как правильно и законно расположить ваши константы в ROM — добро пожаловать под кат.
Информация
- В рейтинге
- Не участвует
- Зарегистрирован
- Активность