x893 35 22 мая, 2020 Опубликовано 22 мая, 2020 · Жалоба Смотрите, изучайте и улучшайте https://github.com/stm32duino/Arduino_Core_STM32 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Serhiy_UA 1 23 мая, 2020 Опубликовано 23 мая, 2020 · Жалоба 10 hours ago, x893 said: Смотрите, изучайте и улучшайте https://github.com/stm32duino/Arduino_Core_STM32 haker_fox и x893, спасибо за информацию! Начал понимать, все не так сложно как казалось:). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Darth Vader 0 23 мая, 2020 Опубликовано 23 мая, 2020 · Жалоба Вам в 3-м сообщении сказали: создайте класс UART, заведите в нем помимо ваших данных, поле указателя на структуру аппаратного UART из .h - файла микроконтроллера, инициализируйте его в конструкторе с параметром. Создайте объекты по количеству аппаратных UARTов, передавая в конструктор нужный указатель. Будут у вас несколько объектов одного класса. А в методах класса обращайтесь к регистрам аппаратного UARTа через этот указатель. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Serhiy_UA 1 23 мая, 2020 Опубликовано 23 мая, 2020 · Жалоба 2 hours ago, Darth Vader said: Вам в 3-м сообщении сказали: создайте класс UART, заведите в нем помимо ваших данных, поле указателя на структуру аппаратного UART из .h - файла микроконтроллера, инициализируйте его в конструкторе с параметром. Создайте объекты по количеству аппаратных UARTов, передавая в конструктор нужный указатель. Будут у вас несколько объектов одного класса. А в методах класса обращайтесь к регистрам аппаратного UARTа через этот указатель. Я так и понял, но Вы, что называется, разложили все конкретно по полочкам. Спасибо! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 17 23 мая, 2020 Опубликовано 23 мая, 2020 · Жалоба 9 minutes ago, Serhiy_UA said: Я так и понял, но Вы, что называется, разложили все конкретно по полочкам. Спасибо! "Разложили все по полочкам" все уже в первых постах, а Darth Vader вообще разжевал в кашу и практически ложку в рот положил. Жевать не надо. Вот уж удивительно будет, если и тут возникнут сложности ))) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Serhiy_UA 1 23 мая, 2020 Опубликовано 23 мая, 2020 · Жалоба 53 minutes ago, Forger said: Вот уж удивительно будет, если и тут возникнут сложности ))) При сложностях спрошу еще. Благодарю всех за разъяснения, особенно Forger за выложенные коды. Эти проекты я делаю для самообразования, спешить мне не куда, разберусь.... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 60 23 мая, 2020 Опубликовано 23 мая, 2020 · Жалоба 2 hours ago, Forger said: Вот уж удивительно будет, если и тут возникнут сложности ))) А они есть. Для меня, например, до сих пор не совсем понятно, что лучше: делать классы драйверов на основе "синлтона", настраивая драйвер в run-time, либо делать на шаблонах, настраивая всё на этапе компиляции.. Но это тонкости. И лень не позволяет мне заняться этим вплотную. Единственное, что сделал, так это убрал из всех синглтонов динамическое выделение памяти с помощью new. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
one_eight_seven 3 23 мая, 2020 Опубликовано 23 мая, 2020 · Жалоба 1 hour ago, haker_fox said: что лучше: делать классы драйверов на основе "синлтона", настраивая драйвер в run-time Зачем тут вообще синглтон? Это же крест на масштабируемости. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 60 23 мая, 2020 Опубликовано 23 мая, 2020 · Жалоба 2 minutes ago, one_eight_seven said: Зачем тут вообще синглтон? Это же крест на масштабируемости. Не совсем понял, что вы имеете в виду? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
one_eight_seven 3 23 мая, 2020 Опубликовано 23 мая, 2020 (изменено) · Жалоба 4 minutes ago, haker_fox said: Не совсем понял, что вы имеете в виду? Синглтон может быть только один. Мне непонятно куда его втыкать в контексте драйверов. Синглтон-драйвер UART'а? Так UART'ов может быть несколько. В том числе несколько одинаковых. Закрывать синглтоном доступ к ресурсам? Опять же - к каким? К выводам - не подходит, их может быть много. К общему буферу - читатель-писатель для обмена данными? Опять же, в ходе работы из одного общего буфера, как правило, выделяются области, являющиеся, по логике использования, такими же буферами, как общий, и их логично делать одинаковыми, а значит, их будет много. Изменено 23 мая, 2020 пользователем one_eight_seven Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 60 23 мая, 2020 Опубликовано 23 мая, 2020 · Жалоба 10 minutes ago, one_eight_seven said: Синглтон может быть только один Ох да, здесь я неточен в терминах (я не очень хорошо разбираюсь в териминологии программирования), скорее всего я имел в виду фабрику-синглтон. Или как правильно это называется? Посмотрите, пожалуйста здесь я привёл пример хидера своего драйвера SPI. В качестве аргумента функции instance передаётся номер шины, с которой мы будем работать. Естественно, что классический "одиночка" здесь не подходит) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
one_eight_seven 3 23 мая, 2020 Опубликовано 23 мая, 2020 (изменено) · Жалоба 17 minutes ago, haker_fox said: Или как правильно это называется? Не знаю. Но ни на фабричный метод, ни на абстрактную фабрику это не похоже. На синглтон похоже, только стандартный конструктор доступен, так что создать ещё экземпляр труда не составит. При этом, создаётся, насколько я вижу статический массив драйверов. Выглядит как переусложнённый массив из драйверов. Какие тут плюсы? экономия памяти на методе instance? Ну и тот самый крест на масштабируемости: При изменении количества SPI необходимо переписывать этот класс - изменять размер массива, добавлять обработчик прерываний. А если поднято автоматическое тестирование, CI/CD, то переписывание тестов. Изменено 23 мая, 2020 пользователем one_eight_seven Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 131 23 мая, 2020 Опубликовано 23 мая, 2020 · Жалоба У меня назрел вопрос, наверное, не совсем по теме. Вот в универе мы когда-то проходили ООП на примере различных программ, где наследование объектов было действительно удобным. А какой смысл вообще делать какие-то универсальные интерфейсы для низкоуровневой работы с физическими интерфейсами МК? Ну, допустим, тот же UART. Ну одинаковым там будет, разве что, инициализация. И то универсальность под вопросом. Например, сегодня нужна инициализация UART и работа по прерываниям, а завтра к этому UART надо приклеить DMA и, соответственно, работать через него. А учитывая громадную вариацию логики взаимодействия этого самого UART с тем же DMA, таймерами или еще чем, затея обобщить интерфейс работы с периферией МК видится не выигрышной. Или я ошибаюсь? Еще ни разу не видел проектов, где "один раз класс написал, и потом тупо используешь". Типа по канонам ООП. Один фиг приходится что-то допиливать, дорабатывать функционал классов и т.д. Пример. Есть железка, которая, помимо своих основных функций, должна подключаться к ПК по UART (отладочный порт). На ПК крутится GUI-программа, вся из себя красивая с кнопочками, визуальными компонентами и текстовой консолью для print() с МК. И вот чтобы добавить эту функциональность в свой проект, я просто создал сначала LL-драйвер dbg.h/dbg.c Спойлер dbg.h #ifndef _DBG_H_ #define _DBG_H_ #include "types.h" #include "stddefs.h" #define DBG_GPIO_RX A, 10 #define DBG_GPIO_TX A, 9 #define DBG_UART_NUM 1 #define DBG_UART_BR 115200 #define DBG_MAXMSG_SIZE 256 typedef enum { DBG_CMD_PRINTNORM = 0, DBG_CMD_PRINTWARN, DBG_CMD_PRINTCRIT, DBG_USRCMD_MINNUM }eDbgCmd; void dbg_Init(void); void dbg_Print(u8 str[], u32 cmd); #define dbg_PrintNorm(s) dbg_Print((u8 *)s, DBG_CMD_PRINTNORM) #define dbg_PrintWarn(s) dbg_Print((u8 *)s, DBG_CMD_PRINTWARN) #define dbg_PrintCrit(s) dbg_Print((u8 *)s, DBG_CMD_PRINTCRIT) u32 dbg_RecvMsg(u8 msg[]); u32 dbg_SendMsg(u8 msg[], u32 len); #endif dbg.c #include <string.h> #include "hw.h" #include "dbg.h" #include "ringbuf.h" #include "stm32f10x.h" #include "retarget.h" #define UART concat2(USART, DBG_UART_NUM) #define UARTIRQ concat3(USART, DBG_UART_NUM, _IRQn) #define EnGPIOClk(d) RCC->APB2ENR |= concat3(RCC_APB2ENR_IOP, _selarg1(d), EN) #if (DBG_UART_NUM == 1) #define EnUARTClk(d) RCC->APB2ENR |= concat3(RCC_APB2ENR_USART, d, EN) #else #define EnUARTClk(d) RCC->APB1ENR |= concat3(RCC_APB1ENR_USART, d, EN) #endif #define DefPinInPEn(d) _CRREG(d) = _CRREG(d) & ~(0xF << _CRPOS(d)) | 0x8 << _CRPOS(d) #define DefPinAltPP(d) _CRREG(d) = _CRREG(d) & ~(0xF << _CRPOS(d)) | 0xB << _CRPOS(d) #define EnPinInPUp(d) _GPIO(d)->ODR |= _PIN(d) #define RxByteUART() UART->DR #define TxByteUART(b) UART->DR = (b) #define EnTxIrqUART() UART->CR1 |= USART_CR1_TXEIE #define DisTxIrqUART() UART->CR1 &= ~USART_CR1_TXEIE #define _GPIO(g, p) GPIO##g #define _PIN(g, p) (1 << (p)) #define _CRPOS(g, p) (4 * ((p) % 8)) #define _CRREG(g, p) *((p) > 7 ? &_GPIO(g, p)->CRH : &_GPIO(g, p)->CRL) #define UARTISR() void concat3(USART, DBG_UART_NUM, _IRQHandler)(void) static sRingBuf RxRingQ, TxRingQ; UARTISR() { u32 isr = UART->SR; u32 icr = UART->CR1; if(icr & isr & USART_SR_RXNE) { static u8 cb = 0, pb = 0; static u32 bcnt = 0, fs = 0; static u8 *msglen[sizeof(bcnt)]; if((cb = RxByteUART()) != pb && pb == 0xC0) ... } ... } void dbg_Init(void) { rb_Init(&RxRingQ); rb_Init(&TxRingQ); EnGPIOClk(DBG_GPIO_RX); EnGPIOClk(DBG_GPIO_TX); EnUARTClk(DBG_UART_NUM); __DSB(); DefPinInPEn(DBG_GPIO_RX); DefPinAltPP(DBG_GPIO_TX); EnPinInPUp(DBG_GPIO_RX); sHWClkFreq freq; hw_GetAllClkFreq(&freq); u32 apbfreq = 0; #if (DBG_UART_NUM == 1) apbfreq = freq.apb[1]; #else apbfreq = freq.apb[0]; #endif UART->BRR = apbfreq / DBG_UART_BR; UART->CR1 |= USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE; NVIC_EnableIRQ(UARTIRQ); NVIC_SetPriority(UARTIRQ, 1); UART->CR1 |= USART_CR1_UE; } void dbg_Print(u8 str[], u32 cmd) { u32 slen = strlen((char *)str) + 1; u32 tlen = slen + sizeof(cmd); if(slen > 1 && rb_GetFree(&TxRingQ) >= sizeof(tlen) + tlen) { rb_Write(&TxRingQ, (RBTYPE *)&tlen, sizeof(tlen)); rb_Write(&TxRingQ, (RBTYPE *)&cmd, sizeof(cmd)); rb_Write(&TxRingQ, str, slen); EnTxIrqUART(); } } u32 dbg_RecvMsg(u8 msg[]) { u32 len = 0; if(rb_GetBusy(&RxRingQ) > sizeof(len)) rb_Peek(&RxRingQ, (RBTYPE *)&len, sizeof(len)); if(len > 0) { if(len <= DBG_MAXMSG_SIZE) { rb_FreeR(&RxRingQ, sizeof(len)); rb_Read(&RxRingQ, (RBTYPE *)msg, len); } else rb_FreeR(&RxRingQ, sizeof(len) + len), len = 0; } return len; } u32 dbg_SendMsg(u8 msg[], u32 len) { u32 rets = 0; if(len > 0 && rb_GetFree(&TxRingQ) >= sizeof(len) + len) { rb_Write(&TxRingQ, (RBTYPE *)&len, sizeof(len)); rb_Write(&TxRingQ, msg, len); EnTxIrqUART(); } else rets = 1; return rets; } Этот драйвер реализует настройку UART и базовые операции отправки/приема данных с буферизацией сообщений. А теперь я создаю, можно сказать, независимое приложение для многопоточной ОС. Реализуется оно в файлах pcdbg.h/pcdbg.c Спойлер pcdbg.h #ifndef _PCDBG_H_ #define _PCDBG_H_ #include "types.h" #include "stddefs.h" void pcdbg_Init(void); #endif pcdbg.c #include "dbg.h" #include "rel.h" #include "eds.h" #include "crc.h" #include "pcdbg.h" #include "verhist.h" typedef enum { CMD_GETINF = DBG_USRCMD_MINNUM, CMD_ONREL, CMD_OFFREL }eCmd; typedef struct { u32 cmd; u32 arg; u32 crc; }sRxMsg; typedef struct { u32 cmd; struct { u32 can1 : 1; u32 can2 : 1; u32 can3 : 1; u32 canbyp : 1; u32 canmode : 2; }rel; u32 snum; s32 tcpu; u32 vcpu; u32 hwv; u32 swv; u32 crc; }sTxMsg; static void RxMsgProc(u32 msg[], u32 len) { sRxMsg *rxmsg = (sRxMsg *)msg; switch(rxmsg->cmd) { case CMD_GETINF: { sTxMsg txmsg; txmsg.cmd = CMD_GETINF; txmsg.rel.can1 = rel_isEnCAN1(); txmsg.rel.can2 = rel_isEnCAN2(); txmsg.rel.can3 = rel_isEnCAN3(); txmsg.rel.canbyp = rel_isBypCAN(); txmsg.rel.canmode = rel_GetCANMode(); txmsg.snum = 0; txmsg.tcpu = 0; txmsg.vcpu = 0; txmsg.hwv = VERHIST_HWVER; txmsg.swv = VERHIST_PACKSWVER; txmsg.crc = crc_CalcSW((u32 *)&txmsg, sizeof(txmsg) / 4 - 1); dbg_SendMsg((u8 *)&txmsg, sizeof(txmsg)); break; } case CMD_ONREL: if(rxmsg->arg <= REL_CANBYP) rel_On(rxmsg->arg); break; case CMD_OFFREL: if(rxmsg->arg <= REL_CANBYP) rel_Off(rxmsg->arg); break; } } static void PCDbgTsk(void) { dbg_PrintNorm("Run PC debug thread"); while(1) { static u32 msg[DBG_MAXMSG_SIZE / 4]; u32 len = dbg_RecvMsg((u8 *)msg) / 4; if(len > 0 && crc_CalcSW(msg, len) == 0) RxMsgProc(msg, len); else eds_Timeout(20); } } void pcdbg_Init(void) { dbg_Init(); eds_CreateTsk(EDS_TSKID_PCDBG, PCDbgTsk, 256); } Все! Логика отладчика добавлена в железку. При желании, может корректироваться независимо от остального проекта. Номер UART и номера прерываний определяются макросами вверху .c-файла, а user-defined окружение (для UART это GPIO да скорость, как у меня) - в соответствующем .h. И чем тогда подход в стиле ООП с классами/наследованием и т.д. лучше, допустим, того, что использую я в чистых Сях? Где тут я мог бы что-то универсализировать? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 17 23 мая, 2020 Опубликовано 23 мая, 2020 · Жалоба 2 minutes ago, Arlleex said: И чем тогда подход в стиле ООП с классами/наследованием и т.д. лучше, допустим, того, что использую я в чистых Сях? Где тут я мог бы что-то универсализировать? Меньше объем текста программы и проще в ней разбираться. Навык приходит только с опытом. з.ы А тем временем тема постепенно превращалась в типичный холивар.... ))) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 60 23 мая, 2020 Опубликовано 23 мая, 2020 · Жалоба 46 minutes ago, one_eight_seven said: Какие тут плюсы? экономия памяти на методе instance? Об этом не думал. Наличие недостатков, как и достоинств не отрицаю. Здесь, скорее, дело привычки. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться