Русский
Русский
English
Статистика
Реклама

Hal

Из песочницы Как получить 9В12В от зарядного с Quick Charge (на примере STM32)

08.11.2020 18:18:22 | Автор: admin

Чем может быть полезна быстрая зарядка


С увеличением ёмкости аккумуляторов телефонов потребовалось увеличить и мощность зарядных устройств, чтобы достичь маленького времени зарядки, для чего и нужно было увеличивать выходную мощность: напряжение, ток. Таким образом зарядные с Quick Charge 3.0 кроме 5 В могут выдавать 9В/12В/20В +возможность регулировки с шагом 0.2 В (до 12 В).


image

Ввиду распространенности ЗУ с этой технологией появляется интерес использовать их для получения повышенного напряжения без дополнительных преобразователей.


Схема подключения


Представленная схема позволит выводам, настроенным как двухтактный выход подавать на выводы DN, DP нужные значения напряжения:


Оба вывода к минусу
0 В
Верхний вывод к плюсу, а нижний к минусу
0.6 В
Оба вывода к плюсу
3.3 В

image

Настройка в STM32CubeMX


Нужно настроить четыре любые выводы общего назначения как двухтактный выход (Output Push Pull) без подтяжки (No pull-up and no pull-down) с соответствующими названиями (ПКМ -> Enter User Label).



Описание протокола Quick Charge


QC 2.0 (из документа CHY100)


image

После включения в сеть замыкаются выводы DP, DP и начинает следить за уровнем на выводе DP, подаем на него напряжение от 0.325 В до 2 В (обычно 0.6 В) на время не менее 1.25 с и таким образом происходит вход в режим Быстрой Зарядки. Теперь на DN нужно подать минус (чтобы напряжение на нем упало ниже 0.325 В) на время не менее 1 мс. Остается выставить сочетание напряжения, соответствующее необходимому, согласно таблице:


image

QC 3.0 (из документа FAN6290Q)


В этой версии есть возможность изменять значение напряжения с шагом 200 мВ, для этого нужно выставить сочетание, соответствующее режиму Continuous Mode:


image

Перейти в него можно из любого другого (5В/9В/12В), а потом для увеличения выходного напряжения (DN: 3.3 В, DP: импульс 0.6-3.3-0.6В), а для уменьшения (DP: 0.6 В, DN: 3.3-0.6-3.3В).


image

Программирование


Остается завернуть изменение уровней сигнала согласно алгоритму в код с использованием библиотеки HAL, учитывая понятные ярлыки-названия, установленные в Кубе:


void QC_GPIO_9V(void){    /* DP: 0.6V; DN: 0.6V - preset */    HAL_GPIO_WritePin(QC_DP_UP_GPIO_Port, QC_DP_UP_Pin, GPIO_PIN_SET);    HAL_GPIO_WritePin(QC_DP_DOWN_GPIO_Port, QC_DP_DOWN_Pin, GPIO_PIN_RESET);    HAL_GPIO_WritePin(QC_DN_UP_GPIO_Port, QC_DN_UP_Pin, GPIO_PIN_SET);    HAL_GPIO_WritePin(QC_DN_DOWN_GPIO_Port, QC_DN_DOWN_Pin, GPIO_PIN_RESET);    HAL_Delay(1250); /* min 1.25s */    /* DP: 0.6V; DN: 0V */    HAL_GPIO_WritePin(QC_DN_UP_GPIO_Port, QC_DN_UP_Pin, GPIO_PIN_RESET);    HAL_Delay(1); /* min 1ms */    /* DP: 3.3V; DN: 0.6V for 9V */    HAL_GPIO_WritePin(QC_DP_UP_GPIO_Port, QC_DP_UP_Pin, GPIO_PIN_SET);    HAL_GPIO_WritePin(QC_DP_DOWN_GPIO_Port, QC_DP_DOWN_Pin, GPIO_PIN_SET);    HAL_GPIO_WritePin(QC_DN_UP_GPIO_Port, QC_DN_UP_Pin, GPIO_PIN_SET);    HAL_GPIO_WritePin(QC_DN_DOWN_GPIO_Port, QC_DN_DOWN_Pin, GPIO_PIN_RESET);}

Таким образом получились функции:


QC_GPIO_5V();QC_GPIO_9V();QC_GPIO_12V();QC_GPIO_20V();QC_GPIO_Reg();QC_GPIO_Dec();QC_GPIO_Inc();

Скачать проект в STM32CubeIDE можно на GitHub: Quick-Charge-STM32-HAL


Проверка работы


Остается подключить всё согласно схеме и выполнить функцию для получения нужного напряжения (для испытания используется безымянная китайская зарядка с QC 3.0):


image

Сработало:


image

Причем выходное напряжение можно изменить в любой момент:


image

При использовании разъема USB Type-C обязательно нужно добавить два резистора 5.1 кОм между CC1, CC2 и GND, чтобы устройство определялось как UFP (Upstream Facing Port).



Определение подключения


В случае, если питание будет подаваться на микроконтроллер уже после подключения, то выполнение нужной функции может выполнятся перед главным циклом один раз.
Если микроконтроллер питается от независимого источника, то выполнение функции можно назначить по внешнему прерыванию (вывод VBUS подключается через стабилизатор 3.3 В) или просто с помощью кнопки можно сделать свой "триггер".


Проверка на разных ЗУ с USB-A и USB-C


Работоспособность проверена на различных недорогих зарядных, а также на мощных ноутбучных зарядок 65Вт с USB Type-C.



При этом наименьшее выходное напряжение может различаться, так обычные имели нижний порог 4.2 В, а продвинутые 3.7 В.



Подробнее в видео



Итого


Хоть стандартом становиться технология Power Delivery (PD), но куча современных сетевых зарядных устройств как и многие переносные аккумуляторные ЗУ поддерживают в том числе Quick Charge (QC), что позволит с легкостью получить повышенное напряжения без использования дополнительных преобразователей.


Несмотря на то, что в теории можно получить даже 20 В, но на практике таких зарядок почти нет. Также стоит учесть, что при подключении слишком мощной нагрузки напряжение будет сильно просаживаться, а некоторые ЗУ вообще уйдут в защиту.

Подробнее..

Из песочницы Вывод текста на OLED дисплей с контроллером SH1106 по шине SPI через библиотеку HAL

23.08.2020 16:12:18 | Автор: admin

Здравствуйте, уважаемые читатели. В своих разработках на микроконтроллерах STM32, для вывода осмысленной информации, я пользуюсь OLED дисплеями на чипе SSD1306. В последний раз пришел ко мне 1,3" SPI модель по демократичной цене около 200руб. Первое, что бросилось в глаза надпись SH1106 вместо SSD1306, поиск в интернете прояснил, что это практически тоже самое, только оставлен единственный страничный режим адресации, да и тот ограничен одной строкой. Как с ним работать я и постараюсь объяснить вам в этой публикации.


Где-то с год назад мне стало не хватать возможностей синей пилюли (STM32F103) и была заказана китайская плата разработчика STM32F407VE. Для отладки, часто, двух светодиодов не хватает, поэтому в каждом проекте для вывода информации подключаю OLED SSD1306 по шине I2C, в который влюбился еще со времен Arduino. Так как графику я на него не вывожу, в основном числа и текст, а размер готовых библиотек и их содержание поражал мое воображение, была написана небольшая библиотечка, которую я немного адаптировал под SH1106 и хочу поделится с вами процессом ее написания. Дисплей приехал 7pin SPI:


sh1106

Плата разработчика у меня такая, но ничего вам не помешает подключить к другой, хоть на STM3F103, для чего HAL и был придуман (разве не так ?):


F407board


Выберем в CubeMX наш STM32F407VE кристалл, обязательно включим режим отладки, иначе потом перешивать придется через UART1. В Clock Configuration выберем резонаторы 8MHz и зададим частоту работы кристалла 168MHz от HSE. При желании можете сконфигурировать на выход ножки PA6 и PA7, к которым подключены светодиоды D2 и D3 (загораются подачей лог 0) для контроля прохождения проблемных точек кода:


RccDebug

На плате SPI1 выведен на колодку NRF24L01 и к нему у меня подключен ESP-PSRAM64H, для дисплея остался SPI3. Выберем его мастером на передачу в DMA режиме и активируем прерывания, настройки будут такие :


SpiAll

Теперь настроим ножки управляющих сигналов DC (данные/команда), RESET (аппаратный сброс) и CS (выбор дисплея) :


Gpio

Таблица соединений :


SH1106 STM32F407
  1. GND GND
  2. VDD 3V3
  3. SCK PC10
  4. SDA PC12
  5. RES PD0
  6. DC PC11
  7. CS PA15

Увеличим Heap и Stack в 2 раза и создадим проект для Atollic, и выберем его запуск. Сразу создадим библиотеку, которую потом будем подключать к своим проектам. В левом окне раскроем папку Src нашего проекта и в меню выберем File->Source File, введем имя нашей библиотеки spi1106.c аналогично создадим File->Header File с именем spi1106.h. Последний перенесем из папки Src в Inc и откроем для редактирования, между #define ic1306_H_ и #endif /* ic1306_H_ */ определим короткие команды для управлением сигналами CD, RESET и CS и функцию инициализации экрана:

#define SPI1106_H_#include "main.h"void sh1106Init (uint8_t contrast, uint8_t bright, uint8_t mirror);#define SH_Command HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_RESET)#define SH_Data HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, GPIO_PIN_SET)#define SH_ResHi HAL_GPIO_WritePin(RES_GPIO_Port, RES_Pin, GPIO_PIN_SET)#define SH_ResLo HAL_GPIO_WritePin(RES_GPIO_Port, RES_Pin, GPIO_PIN_RESET)#define SH_CsHi HAL_GPIO_WritePin(GPIOA, CS_Pin, GPIO_PIN_SET)#define SH_CsLo HAL_GPIO_WritePin(GPIOA, CS_Pin, GPIO_PIN_RESET)#endif /* SPI1106_H_ */

В файле spi1106.c начнем создавать наши функции, вначале подключим main.h, хэдер нашей библиотеки и выбранного канала SPI дисплея:


#include "main.h"#include <spi1106.h>extern SPI_HandleTypeDef hspi3;

Напишем функции пересылки кода команды через SPI при помощи библиотеки HAL:


void SH1106_WC (uint8_t comm){uint8_t temp[1];SH_Command;SH_CsLo;temp[0]=comm;HAL_SPI_Transmit(&hspi3,&temp,1,1);SH_CsHi;}

Функция инициализации с минимальным набором команд:


void sh1106Init (uint8_t contrast, uint8_t bright,uint8_t mirror){SH_ResLo;HAL_Delay(1);SH_ResHi;HAL_Delay(1);SH1106_WC(0xAE); //display offSH1106_WC(0xA8); //--set multiplex ratio(1 to 64)SH1106_WC(0x3F); //SH1106_WC(0x81); //--set contrast control registerSH1106_WC(contrast);if (mirror) {SH1106_WC(0xA0);SH1106_WC(0xC0);}else {SH1106_WC(0xA1);SH1106_WC(0xC8); }SH1106_WC(0xDA);SH1106_WC(0x12);SH1106_WC(0xD3);SH1106_WC(0x00);SH1106_WC(0x40);SH1106_WC(0xD9); //--set pre-charge periodSH1106_WC(bright);SH1106_WC(0xAF); //--turn on SSD1306 panel}

С ней, надеюсь все понятно, первый параметр контрастность (0-255), второй яркость (разбита на два полубайта, комбинации 0xX0 0x0X недопустимы), третий ориентация дисплея (0/1). Для понимания работы дисплея и дальнейших функций вывода изображения советую почитать статью на Датагоре Визуализация для микроконтроллера. Часть 1. OLED дисплей 0.96 (128х64) на SSD1306" и переведенный русский даташит SSD1306.


До бесконечного цикла в main.c проведем инициализацию дисплея :


/* USER CODE BEGIN 2 */sh1106Init (40,0x22,0);/* USER CODE END 2 */

На экране увидите графические узоры типа таких :


noise

В файле spi1106.h напишем определение сразу еще трех функций очистки, печати мелким и средним шрифтом :


void sh1106Clear(uint8_t start, uint8_t stop);void sh1106SmallPrint(uint8_t posx, uint8_t posy, uint8_t *str);void sh1106MediumPrint(uint8_t posx, uint8_t posy,uint8_t *str);

В функции очистки в файле spi1106.c выбор страницы с 0-ой по 7-ю осуществляется командой 0xB0...0xB7 и будет выглядеть так :


void sh1106Clear(uint8_t start, uint8_t stop){ uint32_t *adrclear;uint32_t timep,timec;uint8_t dt[128];adrclear=(uint32_t *)dt;for(uint8_t i=0;i<32;i++) {*adrclear++=0x00;}for (uint8_t m = start; m <= stop; m++){SH1106_WC(0xB0+m);SH1106_WC(2);SH1106_WC(0x10);SH_Data;SH_CsLo;HAL_SPI_Transmit_DMA(&hspi3,dt,128);timec=HAL_GetTick();timep=timec+50;while ((HAL_SPI_GetState(&hspi3) != HAL_SPI_STATE_READY)&&(timec<timep)){timec=HAL_GetTick();}SH_CsHi;}}

До основного цикла в main.c, после инициализации, вставим очистку :


sh1106Clear(0,7);

Можете поиграться с очисткой, задав заполнение своим узором, мы же двигаем далее, вставим в файл spi1106.c ближе к началу сперва маленький шрифт из файла DefaultFonts.c (взял из какой-то ардуиновской библиотеки). Особенностью данного шрифта является, то что он вертикально ориентирован (как раз под структуру отображения байта дисплеями SSD1306/SH1106). В начале массива символов необходимо удалить ненужные нам 4-е служебных байта. Весь фонт вы найдете в архиве библиотеки, в качестве примера, приведу лишь начало:


SmallFont
const uint8_t SmallFont[] =  //  Шрифт  SmallFont{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //001)0x20=032пробел0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, //002)0x21=033!0x00, 0x00, 0x07, 0x00, 0x07, 0x00, //003)0x22=034"0x00, 0x14, 0x7F, 0x14, 0x7F, 0x14, //004)0x23=035#0x00, 0x24, 0x2A, 0x7F, 0x2A, 0x12, //005)0x24=036$0x00, 0x23, 0x13, 0x08, 0x64, 0x62, //006)0x25=037%0x00, 0x36, 0x49, 0x55, 0x22, 0x50, //007)0x26=038&0x00, 0x00, 0x05, 0x03, 0x00, 0x00, //008)0x27=039'0x00, 0x00, 0x1C, 0x22, 0x41, 0x00, //009)0x28=040(0x00, 0x00, 0x41, 0x22, 0x1C, 0x00, //010)0x29=041)0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, //011)0x2A=042*0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, //012)0x2B=043+0x00, 0x00, 0x00, 0xA0, 0x60, 0x00, //013)0x2C=044,0x00, 0x08, 0x08, 0x08, 0x08, 0x08, //014)0x2D=045-0x00, 0x00, 0x60, 0x60, 0x00, 0x00, //015)0x2E=046.0x00, 0x20, 0x10, 0x08, 0x04, 0x02, //016)0x2F=047///0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, //017)0x30=04800x00, 0x00, 0x42, 0x7F, 0x40, 0x00, //018)0x31=04910x00, 0x42, 0x61, 0x51, 0x49, 0x46, //019)0x32=05020x00, 0x21, 0x41, 0x45, 0x4B, 0x31, //020)0x33=05130x00, 0x18, 0x14, 0x12, 0x7F, 0x10, //021)0x34=05240x00, 0x27, 0x45, 0x45, 0x45, 0x39, //022)0x35=05350x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, //023)0x36=05460x00, 0x01, 0x71, 0x09, 0x05, 0x03, //024)0x37=05570x00, 0x36, 0x49, 0x49, 0x49, 0x36, //025)0x38=05680x00, 0x06, 0x49, 0x49, 0x29, 0x1E, //026)0x39=05790x00, 0x00, 0x36, 0x36, 0x00, 0x00, //027)0x3A=058:0x00, 0x00, 0x56, 0x36, 0x00, 0x00, //028)0x3B=059;0x00, 0x08, 0x14, 0x22, 0x41, 0x00, //029)0x3C=060<0x00, 0x14, 0x14, 0x14, 0x14, 0x14, //030)0x3D=061 =0x00, 0x00, 0x41, 0x22, 0x14, 0x08, //031)0x3E=062>0x00, 0x02, 0x01, 0x51, 0x09, 0x06, //032)0x3F=063?//0x00, 0x32, 0x49, 0x59, 0x51, 0x3E, //033)0x40=064@0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, //034)0x41=065A0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, //035)0x42=066B// ...}


Напишем функцию печати маленьким шрифтом высотой одна страница (ширина 6, высота 8 точек), где posx знакоместо, кратное 6 пикселям дисплея, а posy номер страницы (0 верхняя, 7 нижняя):


void sh1106SmallPrint(uint8_t posx, uint8_t posy, uint8_t *str){uint8_t dt[128];uint16_t posfont, posscr;uint32_t *adrclr;uint16_t *adrdst,*adrsrc;uint32_t timer,timec;adrclr=(uint32_t *)&dt;uint8_t code;code=*str++;for(uint8_t i=0;i<32;i++) { *adrclr++=0; }posscr=posx*6;while (code>31){if(code==32) {posscr+=2;}else{posfont=6*(code-32);adrdst=(uint16_t *)&dt[posscr];adrsrc=(uint16_t *)&SmallFont[posfont];*(adrdst++)=*(adrsrc++);*(adrdst++)=*(adrsrc++);*(adrdst++)=*(adrsrc++);posscr+=6;}code=*str++;if (posscr>122) break;}SH1106_WC(0xB0+posy);SH1106_WC(2);SH1106_WC(0x10);SH_Data;SH_CsLo;HAL_SPI_Transmit_DMA(&hspi3,dt,128);timec=HAL_GetTick();timer=timec+50;while ((HAL_SPI_GetState(&hspi3) != HAL_SPI_STATE_READY)&&(timec<timer)){timec=HAL_GetTick();}SH_CsHi;}

Затем, в библиотеке spi1106.c после мелкого шрифта вставим средний (высота 16, ширина 12 пикселей) из того же файла DefaultFonts.c (также удалите 4 служебных байта в начале массива) и напишем функцию печати средним шрифтом высотой две страницы, где posx знакоместо, кратное уже 12 пикселям дисплея, а posy аналогично предыдущей функции номер страницы:


void sh1106MediumPrint(uint8_t posx, uint8_t posy, uint8_t *str){uint8_t dt[256];uint16_t posfont, posscr;uint32_t *adrdst, *adrsrc;uint32_t timer,timec;adrdst=(uint32_t *)&dt;uint8_t code;code=*str++;for(uint8_t i=0;i<64;i++) { *adrdst++=0; }posscr=posx*12;while (code>31){posfont=24*(code-32);adrsrc=(uint32_t *)&MediumFont[posfont];adrdst=(uint32_t *)&dt[posscr];*(adrdst++)=*(adrsrc++);*(adrdst++)=*(adrsrc++);*(adrdst++)=*(adrsrc++);adrsrc=(uint32_t *)&MediumFont[posfont+12];adrdst=(uint32_t *)&dt[posscr+128];*(adrdst++)=*(adrsrc++);*(adrdst++)=*(adrsrc++);*(adrdst++)=*(adrsrc++);code=*str++;posscr+=12;if (posscr>116) break;}SH1106_WC(0xB0+posy);SH1106_WC(2);SH1106_WC(0x10);SH_Data;SH_CsLo;HAL_SPI_Transmit_DMA(&hspi3,dt,128);timec=HAL_GetTick();timer=timec+50;while ((HAL_SPI_GetState(&hspi3) != HAL_SPI_STATE_READY)&&(timec<timer)){timec=HAL_GetTick();}SH1106_WC(0xB0+posy+1);SH1106_WC(2);SH1106_WC(0x10);SH_Data;SH_CsLo;HAL_SPI_Transmit_DMA(&hspi3,dt+128,128);timec=HAL_GetTick();timer=timec+50;while ((HAL_SPI_GetState(&hspi3) != HAL_SPI_STATE_READY)&&(timec<timer)){timec=HAL_GetTick();}SH_CsHi;}

В main.c после очистки напечатаем приветствие :


sh1106SmallPrint(0,0,(uint8_t *) "Hello SH1106_1234567890");sh1106MediumPrint(0,1,(uint8_t *) "Hi SH1106");sh1106MediumPrint(0,3,(uint8_t *) "Hello SH1106");

hello

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


И в завершение определим скорость обновления всего экрана, в главном цикле напишем такую программу, которая заполняет по одному пикселю буфер экрана и затем обновляет 7 страниц и печатает на последней строке время в миллисекундах :


/* Infinite loop *//* USER CODE BEGIN WHILE */uint8_t buf[128*8];char str[32];uint16_t count;uint8_t x,y,b;uint32_t timep,timec;while (1){count++;b=count&0x07;x=(count>>3)&0x7f;y=(count>>10)&0x07;buf[y*128+x]=buf[y*128+x]|(1<<b);timec=HAL_GetTick();for (uint8_t m = 0; m < 7; m++){SH1106_WC(0xB0+m);SH1106_WC(2);SH1106_WC(0x10);SH_Data;SH_CsLo;HAL_SPI_Transmit_DMA(&hspi3,buf+m*128,128);while ((HAL_SPI_GetState(&hspi3) != HAL_SPI_STATE_READY)){__NOP();}SH_CsHi;}timep=HAL_GetTick();sprintf(str, "%d", timep-timec);sh1106SmallPrint(0,7,str);/* USER CODE END WHILE */

speed

По скорости заполнения примерно одна страница в секунду и мелькающим в нижней строке то "0" то "1" можно сказать, что достигнута скорость обновления семи строк экрана менее 1ms, т.е. около 1000fps. Конечно с учетом реальных задач построения изображения в буфере, будет медленнее, но результат впечатляет. Да и задержку ожидания можно вставить до пересылки по SPI и распаралелить задачи отрисовки МК и перекидывания по SPI в режиме DMA.


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

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru