Продолжаем работать с АЦП (ADC) контроллера STM32F1 с использованием библиотеки LL. Также работать мы пока будем с однократным преобразованием, но уже использовать мы будем не регулярные каналы, а инжектированые. Про данную группу я кратко говорил в предыдущих уроках, также мы её использовали давно на другом контроллере в уроке 19 и в уроке 20. Тем не менее напомню, что главное преимущество инжектированной группы каналов над регулярной в том, что для каждого канала существует свой регистр и нам не обязательно резко считать данные из регистра, чтобы они не затёрлись после конверсии одного канала, а лишь после этого приступать к конверсии другого.
Осваивать инжектированную группу мы начнём также постепенно, лучше пусть уроки будут небольшими, но с узкой темой, а чтобы не затянуть курс надолго, я их буду постить почаще.
На данном уроке мы воспользуемся обычным однократным преобразованием без последовательности, поэтому подключим мы только один канал, хотя схему мы оставим с прошлого урока, просто использоваться будет только один вход
С регистрами и их полями, используемыми для инжектированной группы, мы также знакомились в уроке 185, поэтому, думаю, мы сразу можем приступить к проекту урока, который был сделан из проекта того же урока с именем LL_ADC_REG_ONCE и назван LL_ADC_INJ_ONCE.
Откроем наш проект в Cube MX и в настройках ADC1 отключим регулярную группу, включим инжектированную, добавим только один канал, также время между выборками установим в 7,5 циклов
Сгенерируем проект для Keil, откроем его, настроим автоперезагрузку после прошивки, отключим оптимизацию, подключим к дереву проекта файлы lcd.c и i2c_user.c, откроем main.c и, наверно, не стоило бы вообще смотреть инициализацию ADC, которая сгенерировалась в Cube и я хотел всё это для экономии времени оставить на самостоятельный разбор, но не тут-то было. Как обычно, здесь не без сюрпризов, поэтому давайте всё-же глянем вот на этот участок функции MX_ADC1_Init
LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
Как мы видим, непонятно для чего здесь объявляется структура для регулярной группы, а не для инжекторной с именем LL_ADC_INJ_InitTypeDef.
А далее данная структура инициализируется и, соответственно, включаются все регистры также для настройки регулярных каналов
ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_DISABLE;
ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE;
ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_NONE;
LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
Лишь настройки самого канала добавляются как положено — для инжектированной группы
LL_ADC_INJ_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_1);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_1, LL_ADC_SAMPLINGTIME_7CYCLES_5);
LL_ADC_INJ_SetOffset(ADC1, LL_ADC_REG_RANK_1, 0);
Возможно, что-то недоглядели создатели Cube, также я думал, может, если проект делать с нуля, а не из донорского, то может включатся инжектированные настройки, но нет, всё также. Поэтому с нуля мы создавать проект не будем, зачем нам заново настраивать дисплей, i2c, добавлять для него библиотеки. Впрочем, опять же, решать вам.
В связи со всем этим все настройки мы внесём самостоятельно в функции main(), в которой для начала, чтобы сбросить все регулярные настройки, применим деинициализацию периферии
1 2 |
/* USER CODE BEGIN 2 */ LL_ADC_DeInit(ADC1); |
А теперь настроим ADC на работу с инжектированной группой
1 2 3 4 5 6 7 |
LL_ADC_DeInit(ADC1); //Set ADC group injected trigger source LL_ADC_INJ_SetTriggerSource(ADC1, LL_ADC_INJ_TRIG_SOFTWARE); //Configure Injected Channel LL_ADC_INJ_SetSequencerRanks(ADC1, LL_ADC_INJ_RANK_1, LL_ADC_CHANNEL_1); LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_1, LL_ADC_SAMPLINGTIME_7CYCLES_5); LL_ADC_INJ_SetOffset(ADC1, LL_ADC_INJ_RANK_1, 0); |
Вот здесь изменим надпись
LCD_String("Jnjected Once");
В бесконечном цикле изменим имя вызываемой функции старта АЦП
LL_ADC_INJ_StartConversionSWStart(ADC1);
В анализе флага окончания преобразования также изменим имена вызываемых функций, для чего в них достаточно будет добавить лишь одну букву
while (!LL_ADC_IsActiveFlag_JEOS(ADC1)) {}
LL_ADC_ClearFlag_JEOS(ADC1);
В следующей функции, которая забирает данные из регистра, мы не только исправим имя, но и в качестве ещё одного аргумента добавим номер канала, так как мы уже знаем, что у каналов инжектированной группы имеется у каждого свой регистр
ADC_Data = LL_ADC_INJ_ReadConversionData12(ADC1, LL_ADC_INJ_RANK_1);
Если мы заглянем внутрь данной функции, то тем самым мы забираем сырые данные из регистра ADC1->JDR1.
Соберём код, прошьём контроллер и покрутим движок резистора, проследим, как будет изменяться результат в вольтах на дисплее
Итак, на данном уроке мы научились настраивать инжекторный канал АЦП, а также снимать с него показания.
Всем спасибо за внимание!
Предыдущий урок Программирование МК STM32 Следующий урок
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Переходник I2C to LCD можно приобрести здесьI2C to LCD1602 2004
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Добавить комментарий