Arlleex 129 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба Приветствую! До этого момента описывал периферию МК как натянутую на нужные адреса регистровую модель этой самой периферии. Что-то типа #define CPC ((volatile sCPC *)0x12345678) ... typedef struct { const u32 SR; u32 CR; ... }sCPC; Работа с битами осуществляется через определенные макросы; вот так, например, осуществляется атомарная запись в регистр двух битовых полей CPC->PLL0.CR2 = maskbits(CPC->PLL0.CR2, ~(PLL0CR2_N | PLL0CR2_M)) | makefield(PLL0CR2_N, PLL0_N - 1UL) | makefield(PLL0CR2_M, PLL0_M - 1UL); Это удобно читать, потому что элементарные действия над битовыми полями спрятаны за говорящими #defines. Однако такой подход требует создавать кучу #defines для битовых масок, позиций бит, их возможных значений и т.д. Возникает вероятность банальной коллизии имен, если где-то в другой периферии окажется регистр с таким же именем. Можно, конечно, сделать все имена уникальными, приписав, например, вначале определения название периферии (например, CPC_SR_CLKEN). Но исходник получается реально загроможденным и слабо читаемым. Короче, не комильфо. Очевидно, можно описать нужные биты битовыми полями структур, но есть нюанс: атомарную запись в несколько битовых полей я произвести не могу (по крайней мере так, чтобы это было красиво). Может есть какой-нибудь трюк, все-таки позволяющий атомарно записать несколько битовых полей структуры? Имеется, допустим, // битовые представления регистров typedef union { struct { u32 M : 15UL; u32 __RESERVE1__ : 1UL; u32 N : 8UL; u32 EN : 1UL; u32 SEL : 1UL; u32 RDY : 1UL; u32 __RESERVE2__ : 5UL; }; u32 RAW; }sPLL0СR; ... // описание регистров периферии typedef struct { ... sPLL0СR СR; ... }sPPL0; Теперь я хочу одним махом каким-то образом сразу инициализировать поля M и N в структуре CR. Возможно ли такое средствами языка? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 17 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба 59 minutes ago, Arlleex said: Теперь я хочу одним махом каким-то образом сразу инициализировать поля M и N в структуре CR. Возможно ли такое средствами языка? В принципе вполне возможно, но тогда придется руками описывать все эти поля. Имхо, очень трудоемко это. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 172 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба 2 часа назад, Arlleex сказал: Может есть какой-нибудь трюк, все-таки позволяющий атомарно записать несколько битовых полей структуры? Если речь про Cortex-M, то конечно сеть: LDREX/STREX. А "средствами языка" - вряд-ли, потому что язык си не имеет отношения к системе команд - на одной платформе что-то будет атомарным, а на другой - оно уже может быть и не атомарным. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 129 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба Упс. Тут под атомарностью я имел ввиду не механизм доступа к памяти, а единое выражение Си-кода, не более Вот пример. Есть структура, определяющая биты в регистре union { struct { u32 a : 4; u32 b : 5; u32 : 1; u32 c : 6; u32 : 8; u32 d : 8; }; u32 REG; }S; Допустим, я хочу записать a = 5 и c = 10, оставляя содержимое резервных бит неизменным (так требует аппаратура). Для этого, используя только определение структуры, путь только один: две последовательные записи S.a = 5; S.c = 10; Используя только определение структуры как набор битовых полей, в таких #defines, как определяющих битовые маски #define A_MASK 0x0000000FUL #define B_MASK 0x000001F0UL #define C_MASK 0x0000FCFFUL #define D_MASK 0xFF000000UL не будет необходимости. Это избавит от вереницы #defines, которые для разной периферии могут, к сожалению, совпасть по имени и испортить всю малину (красоту кода). Но минус битовых полей структуры в том, что за один раз можно изменить только одно поле. А в случае с за-#define-нными полями - можно менять сколько угодно S.REG = maskbits(S.REG, ~(A_MASK | C_MASK)) | makefield(A_MASK, 5UL) | makefield(C_MASK, 10UL); , где maskbits() - макрос взятия результата маскировки от значения, а makefield() - макрос расчета позиции битового поля и записи значения в это битовое поле по известной маске. Вот и получается, что способ с битовыми полями структур обладает хорошим "пространством имен" (не нужны #defines отдельных бит и масок), что точно избавит от конфликта имен при сборке проекта из множества файлов, где имена управляющих регистров периферии могут совпасть (и названия битов тоже); в то же время нельзя одним махом хитро записать сразу в два поля этой структуры (используя только имена битовых полей, иначе фишка теряется). А у способа с #defines всех битов и масок все ровно наоборот. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 172 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба 3 минуты назад, Arlleex сказал: в то же время нельзя одним махом хитро записать сразу в два поля этой структуры (используя только имена битовых полей, иначе фишка теряется). А у способа с #defines всех битов и масок все ровно наоборот. Скопируйте в локальную переменную и присвойте в ней, а потом - обратно. Хотя сишных операций всё равно будет столько же, но количество команд компилятор возможно оптимизирует. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 17 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба 7 minutes ago, Arlleex said: Упс. Тут под атомарностью я имел ввиду не механизм доступа к памяти, а единое выражение Си-кода, не более Единственный вариант (если уж так принципиально писать разом) - писать сразу весь регистр, БЕЗ битовых полей. Принцип чтение-модификация-запись. Честно говоря, у меня тоже была похожая мысль - обращаться к полям регистров по вот таким union, но прикинув объем в целом бессмысленной работы, я отказался. Ограничился лишь прямым доступом через уже прописанные поля и флаги, поставляемые от разработчика контроллера в соотв. h-файлах (куб и spl hal - в топку). Например: #define SET_BIT(REG, BIT) ((REG) |= (BIT)) #define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT)) #define READ_BIT(REG, BIT) ((REG) & (BIT)) В данном случае эти макросы уже есть в каждом h-файле у тех же ST (я про файлы, где расписаны регистры, маски и флаги на соотв. семейство). Их поэтому не нужно объявлять. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 129 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба 9 минут назад, jcxz сказал: Скопируйте в локальную переменную и присвойте в ней, а потом - обратно. Хотя сишных операций всё равно будет столько же, но количество команд компилятор возможно оптимизирует. Угу. Видимо, только так. Скорее всего, придется все-таки по старинке, с помощью #defines. Придумаю, как оптимально их обзывать, чтобы они никогда не повторялись в проекте. 8 минут назад, Forger сказал: В данном случае эти макросы уже есть в каждом h-файле у тех же ST (я про файлы, где расписаны регистры, маски и флаги на соотв. семейство). Их поэтому не нужно объявлять. Да это понятно... Но я все-таки отвожу себе времени на собственное описание периферии - оно и в голове лучше укладывается, и свой код потом приятно смотреть. А собственных макросов, облегчающих жизнь, у меня не меньше, чем дофига Спасибо всем! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 17 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба 2 minutes ago, Arlleex said: Угу. Видимо, только так. Скорее всего, придется все-таки по старинке, с помощью #defines. Придумаю, как оптимально их обзывать, чтобы они никогда не повторялись в проекте. Да это понятно... Но я все-таки отвожу себе времени на собственное описание периферии - Аналогично! Но использую все эти уже прописанные маски, так как они уже есть. Вот для примера как выглядит настройка (STM32F4) тактирования и шин: Spoiler void ...... ::initializeCore() { Hardware::Power::initialize(); Hardware::Flash::initialize(); Hardware::Clock::initialize(); Hardware::Clock::HSE::setQuartzFrequencyHz(HSE_FREQUENCY_HZ); Hardware::Clock::HSE::enable(); Hardware::Clock::HSE::waitForReady(); Hardware::Clock::HSE::setAsClockSource(); Hardware::Clock::AHB::setPrescaler(Hardware::Clock::AHB::Prescaler::NONE); Hardware::Clock::APB1::setPrescaler(Hardware::Clock::APB1::Prescaler::PRE_4); // Max 42 MHz Hardware::Clock::APB2::setPrescaler(Hardware::Clock::APB2::Prescaler::PRE_2); // Max 84 MHz Hardware::Clock::PLL::setClockSource(Hardware::Clock::PLL::HSE); Hardware::Clock::PLL::setPrescaler(HSE_FREQUENCY_MHZ / PLL_INPUT_FREQUENCY_MHZ); Hardware::Clock::PLL::setMultiplier(PLL_OUTPUT_FREQUENCY_MHZ / PLL_INPUT_FREQUENCY_MHZ); Hardware::Clock::PLL::setSysClockDevider(PLL_OUTPUT_FREQUENCY_MHZ / CORE_FREQUENCY_MHZ); Hardware::Clock::PLL::setupUsbClock(); // 48 MHz only Hardware::Clock::PLL::enable(); Hardware::Clock::PLL::waitForReady(); Hardware::Clock::PLL::setAsClockSource(); Hardware::Flash::setLatency(); Hardware::Flash::Cache::enable(); // Setup OS settings osSettings.coreFrequencyHz = Hardware::Clock::getSystemFrequencyHz(); osSettings.systemTimerFrequencyHz = RTOS_SYSTEM_TIMER_FREQUENCY_HZ; osSettings.applicationName = ..... osSettings.cpuChipName = "STM32F407VGT6"; } Hardware тут заменяется автоматом на соотв пространство имен, в данном случае Hardware=stm32f4 Вот реализация одного из методов: void stm32f4::Clock::HSI::setAsClockSource() { CLEAR_BIT(RCC->CFGR, RCC_CFGR_SW); SET_BIT(RCC->CFGR, RCC_CFGR_SW_HSI); while (READ_BIT(RCC->CFGR, RCC_CFGR_SWS_HSI) != 0); // Wait till HSI is used as system clock source } Внутри активно использую готовые маски и битовые поля, уже любезно предоставленные ST. Не вижу смысла уже тут - внутри моей библиотеки - делать какие-то особые красивости :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 129 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба 16 минут назад, Forger сказал: Вот для примера как выглядит настройка (STM32F4) тактирования и шин... А вот мой инициализатор тактирования LPC1768 (правда еще не дописан чутка) Спойлер void cpc_SysClkInit(void) { resetbits(CPC->PLL0.CR1, CPC_PLLCR1_SEL); UpdatePLL(PLL0); resetbits(CPC->PLL0.CR1, CPC_PLLCR1_EN); UpdatePLL(PLL0); updatefield(CPC->CPUDIVR, CPC_CPUDIVR_DIV, 0UL); #if (defined CPC_CLKSRC_SYS_IRCOSC || defined CPC_CLKSRC_PLL0_IRCOSC) updatefield(CPC->CLKSRCR, CPC_CLKSRCR_SEL, CPC_CLKSRCR_SELIRCOSC); #elif (defined CPC_CLKSRC_SYS_RTCOSC || defined CPC_CLKSRC_PLL0_RTCOSC) updatefield(CPC->CLKSRCR, CPC_CLKSRCR_SEL, CPC_CLKSRCR_SELRTCOSC); #elif (defined CPC_CLKSRC_SYS_EXTOSC ||\ defined CPC_CLKSRC_PLL0_EXTOSC ||\ defined CPC_CLKSRC_USB_PLL1) if(sc_ExtOscEnable()) trap(EMPTY); #endif #if (defined CPC_CLKSRC_SYS_EXTOSC || defined CPC_CLKSRC_PLL0_EXTOSC) updatefield(CPC->CLKSRCR, CPC_CLKSRCR_SEL, CPC_CLKSRCR_SELEXTOSC); #endif #ifdef CPC_CLKSRC_SYS_PLL0 CPC->PLL0.CR2 = maskbits(CPC->PLL0.CR2, ~(CPC_PLL0CR2_N | CPC_PLL0CR2_M)) | makefield(CPC_PLL0CR2_N, CPC_PLL0_N - 1UL) | makefield(CPC_PLL0CR2_M, CPC_PLL0_M - 1UL); setbits(CPC->PLL0.CR1, CPC_PLLCR1_EN); UpdatePLL(PLL0); #endif #ifdef CPC_CLKSRC_USB_PLL0 updatefield(CPC->USBDIVR, CPC_USBDIVR_DIV, CPC_CLKDIV_USB - 1UL); #elif defined CPC_CLKSRC_USB_PLL1 CPC->PLL1.CR2 = maskbits(CPC->PLL1.CR2, ~(CPC_PLL1CR2_M | CPC_PLL1CR2_P)) | makefield(CPC_PLL1CR2_M, CPC_PLL1_M - 1UL) | makefield(CPC_PLL1CR2_P, PLL1_P - 1UL); setbits(CPC->PLL1.CR1, CPC_PLLCR1_EN); UpdatePLL(PLL1); #endif CPC->PCLKDIVR1 = maskbits(CPC->PCLKDIVR1, ~CPC_PCLKDIVR1_MASK) | makefield(CPC_PCLKDIVR1_WDT, CPC_PCLKDIV1(CPC_CLKDIV_WDT)) | makefield(CPC_PCLKDIVR1_TIM0, CPC_PCLKDIV1(CPC_CLKDIV_TIM0)) | makefield(CPC_PCLKDIVR1_TIM1, CPC_PCLKDIV1(CPC_CLKDIV_TIM1)) | makefield(CPC_PCLKDIVR1_UART0, CPC_PCLKDIV1(CPC_CLKDIV_UART0)) | makefield(CPC_PCLKDIVR1_UART1, CPC_PCLKDIV1(CPC_CLKDIV_UART1)) | makefield(CPC_PCLKDIVR1_PWM, CPC_PCLKDIV1(CPC_CLKDIV_PWM)) | makefield(CPC_PCLKDIVR1_I2C0, CPC_PCLKDIV1(CPC_CLKDIV_I2C0)) | makefield(CPC_PCLKDIVR1_SPI, CPC_PCLKDIV1(CPC_CLKDIV_SPI)) | makefield(CPC_PCLKDIVR1_SSP1, CPC_PCLKDIV1(CPC_CLKDIV_SSP1)) | makefield(CPC_PCLKDIVR1_DAC, CPC_PCLKDIV1(CPC_CLKDIV_DAC)) | makefield(CPC_PCLKDIVR1_ADC, CPC_PCLKDIV1(CPC_CLKDIV_ADC)) | makefield(CPC_PCLKDIVR1_CAN1, CPC_PCLKDIV2(CPC_CLKDIV_CAN)) | makefield(CPC_PCLKDIVR1_CAN2, CPC_PCLKDIV2(CPC_CLKDIV_CAN)) | makefield(CPC_PCLKDIVR1_CANF, CPC_PCLKDIV2(CPC_CLKDIV_CAN)); CPC->PCLKDIVR2 = maskbits(CPC->PCLKDIVR2, ~CPC_PCLKDIVR2_MASK) | makefield(CPC_PCLKDIVR2_QEI, CPC_PCLKDIV1(CPC_CLKDIV_QEI)) | makefield(CPC_PCLKDIVR2_EXTI, CPC_PCLKDIV1(CPC_CLKDIV_EXTI)) | makefield(CPC_PCLKDIVR2_PIO, CPC_PCLKDIV1(CPC_CLKDIV_PIO)) | makefield(CPC_PCLKDIVR2_I2C1, CPC_PCLKDIV1(CPC_CLKDIV_I2C1)) | makefield(CPC_PCLKDIVR2_SSP0, CPC_PCLKDIV1(CPC_CLKDIV_SSP0)) | makefield(CPC_PCLKDIVR2_TIM2, CPC_PCLKDIV1(CPC_CLKDIV_TIM2)) | makefield(CPC_PCLKDIVR2_TIM3, CPC_PCLKDIV1(CPC_CLKDIV_TIM3)) | makefield(CPC_PCLKDIVR2_UART2, CPC_PCLKDIV1(CPC_CLKDIV_UART2)) | makefield(CPC_PCLKDIVR2_UART3, CPC_PCLKDIV1(CPC_CLKDIV_UART3)) | makefield(CPC_PCLKDIVR2_I2C2, CPC_PCLKDIV1(CPC_CLKDIV_I2C2)) | makefield(CPC_PCLKDIVR2_I2S, CPC_PCLKDIV1(CPC_CLKDIV_I2S)) | makefield(CPC_PCLKDIVR2_RIT, CPC_PCLKDIV1(CPC_CLKDIV_RIT)) | makefield(CPC_PCLKDIVR2_SC, CPC_PCLKDIV1(CPC_CLKDIV_SC)) | makefield(CPC_PCLKDIVR2_MCPWM, CPC_PCLKDIV1(CPC_CLKDIV_MCPWM)); #ifdef CPC_CLKSRC_SYS_PLL0 for(u32 i = 0UL; i < PLL0_STAB_TIME; ++i) if(bitset(CPC->PLL0.SR, CPC_PLL0SR_RDY)) break; #endif #ifdef CPC_CLKSRC_USB_PLL1 for(u32 i = 0UL; i < PLL1_STAB_TIME; ++i) if(bitset(CPC->PLL1.SR, CPC_PLL1SR_RDY)) break; if(bitreset(CPC->PLL1.SR, CPC_PLL1SR_RDY)) trap(EMPTY); setbits(CPC->PLL1.CR1, CPC_PLLCR1_SEL); UpdatePLL(PLL1); #endif // настройка тактов ожидания Flash (дописать) updatefield(CPC->CPUDIVR, CPC_CPUDIVR_DIV, CPC_CLKDIV_CPU - 1UL); #ifdef CPC_CLKSRC_SYS_PLL0 if(bitreset(CPC->PLL0.SR, CPC_PLL0SR_RDY)) trap(EMPTY); setbits(CPC->PLL0.CR1, CPC_PLLCR1_SEL); UpdatePLL(PLL0); #endif #ifdef CPC_USE_EXTCLKOUT CPC->CLKOUTCR = maskbits(CPC->CLKOUTCR, ~CPC_CLKOUTCR_MASK) #ifdef CPC_CLKSRC_EXTOUT_IRCOSC | makefield(CPC_CLKOUTCR_SRC, CPC_CLKOUTCR_SRCIRC) #elif defined CPC_CLKSRC_EXTOUT_RTCOSC | makefield(CPC_CLKOUTCR_SRC, CPC_CLKOUTCR_SRCRTC) #elif defined CPC_CLKSRC_EXTOUT_EXTOSC | makefield(CPC_CLKOUTCR_SRC, CPC_CLKOUTCR_SRCEXT) #elif defined CPC_CLKSRC_EXTOUT_USBCLK | makefield(CPC_CLKOUTCR_SRC, CPC_CLKOUTCR_SRCUSB) #elif defined CPC_CLKSRC_EXTOUT_CPUCLK | makefield(CPC_CLKOUTCR_SRC, CPC_CLKOUTCR_SRCCPU) #endif | makefield(CPC_CLKOUTCR_DIV, CPC_CLKDIV_EXTOUT - 1UL); cpc_ClkOutCmd(ENABLE); #endif } Функция собирается по частям под необходимые настройки проекта При этом вверху в .c-файле еще целый раздел по статической проверке корректности настроек - чтобы при использовании не задуть частоту CPU на диапазон PLL, например... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 17 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба 1 minute ago, Arlleex said: А вот мой инициализатор тактирования LPC1768 (правда еще не дописан чутка) "Не дописан" говорите? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 129 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба Только что, Forger сказал: "Не дописан" говорите? Да, совсем маленько - в закомментированном месте нужно настроить циклы ожидания доступа к Flash, ну и (возможно) каким-нибудь таймером следить за timeout-ом установки PLL Lock (сейчас, как видно, задержка на for()-е)... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 17 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба 2 minutes ago, Arlleex said: Да, совсем маленько - в закомментированном месте нужно настроить циклы ожидания доступа к Flash, ну и (возможно) каким-нибудь таймером следить за timeout-ом установки PLL Lock (сейчас, как видно, задержка на for()-е)... Для этого существует соотв. прерывание. Или поллить соотв. флажок. Кстати, у меня циклы ожидания flash вот так настраиваются: Spoiler // "Memory.сpp" void stm32f4::Flash::setLatency(Latency latency) { auto flashACR = FLASH->ACR; CLEAR_BIT(flashACR, FLASH_ACR_LATENCY); if (latency > 7) { static auto systemFrequencyMHz = (stm32f4::Clock::getSystemFrequencyHz() / 1000000U); if (systemFrequencyMHz <= 30) SET_BIT(flashACR, FLASH_ACR_LATENCY_0WS); else if (systemFrequencyMHz <= 60) SET_BIT(flashACR, FLASH_ACR_LATENCY_1WS); else if (systemFrequencyMHz <= 90) SET_BIT(flashACR, FLASH_ACR_LATENCY_2WS); else if (systemFrequencyMHz <= 120) SET_BIT(flashACR, FLASH_ACR_LATENCY_3WS); else if (systemFrequencyMHz <= 150) SET_BIT(flashACR, FLASH_ACR_LATENCY_4WS); else if (systemFrequencyMHz <= 180) SET_BIT(flashACR, FLASH_ACR_LATENCY_5WS); else SET_BIT(flashACR, FLASH_ACR_LATENCY_6WS); } else { SET_BIT(flashACR, (static_cast<uint32_t>(latency)) << FLASH_ACR_LATENCY_Pos); } FLASH->ACR = flashACR; } // "Memory.hpp" namespace stm32f4 { class Flash { public: static void initialize(); using Latency = uint8_t; static void setLatency(Latency latency = 255); .... Применение элементарное: Hardware::Flash::setLatency(); короче, суть простая - все сложное и трудночитаемое "прячем" внутрь библиотеки, отлаживаем и забываем. Вспоминаем лишь тогда, когда портируем это на другое семейство или камень. Наружу в user code даем "голую элементарщину". А все начиналось с банального class Pin ... Впрочем, как и у других, кто затеял подобные дела ))) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 17 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба Если в объявлении нет слов volatile, то компилятор может свернуть запись нескольких битовых полей в одно чтение-модификация-запись. Не проверяли? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlexandrY 2 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба 20 minutes ago, Arlleex said: А вот мой инициализатор тактирования LPC1768 (правда еще не дописан чутка) Функция собирается по частям под необходимые настройки проекта При этом вверху в .c-файле еще целый раздел по статической проверке корректности настроек - чтобы при использовании не задуть частоту CPU на диапазон PLL, например... Жесть. Как много символов. В таких местах, если конечно вы хозяин всего проекта, не стоит стеснятся магических цифр и лучше писать все как есть по даташиту. Типа так: R_S12ADC0->ADCSR = 0 + LSHIFT(0, 15) // ADST | A/D Conversion Start | 1: Start A/D conversion process. + LSHIFT(0, 13) // ADCS[1:0] | Scan Mode Select | 0 0: Single scan mode, 0 1: Group scan mode, 1 0: Continuous scan mode, 1 1: Setting prohibited + LSHIFT(1, 9) // TRGE | Trigger Start Enable | 1: Enable A/D conversion to be started by the synchronous or asynchronous trigger. + LSHIFT(0, 8) // EXTRG | Trigger Select | 0: Start A/D conversion by a synchronous trigger (ELC), 1: Start A/D conversion by the asynchronous trigger (ADTRGi). + LSHIFT(0, 7) // DBLE | Double Trigger Mode | 0: Deselect double-trigger mode, 1: Select double-trigger mode. + LSHIFT(0, 6) // GBADIE | Group B Scan | 1: Enable ADC12i_GBADI interrupt generation on Group B scan completion + LSHIFT(0, 0) // DBLANS[4:0] | Double Trigger Channel Select ; R_S12ADC1->ADCSR = 0 + LSHIFT(0, 15) // ADST | A/D Conversion Start | 1: Start A/D conversion process. + LSHIFT(0, 13) // ADCS[1:0] | Scan Mode Select | 0 0: Single scan mode, 0 1: Group scan mode, 1 0: Continuous scan mode, 1 1: Setting prohibited + LSHIFT(1, 9) // TRGE | Trigger Start Enable | 1: Enable A/D conversion to be started by the synchronous or asynchronous trigger. + LSHIFT(0, 8) // EXTRG | Trigger Select | 0: Start A/D conversion by a synchronous trigger (ELC), 1: Start A/D conversion by the asynchronous trigger (ADTRGi). + LSHIFT(0, 7) // DBLE | Double Trigger Mode | 0: Deselect double-trigger mode, 1: Select double-trigger mode. + LSHIFT(0, 6) // GBADIE | Group B Scan | 1: Enable ADC12i_GBADI interrupt generation on Group B scan completion + LSHIFT(0, 0) // DBLANS[4:0] | Double Trigger Channel Select Потому что когда дело доходит до паранойи отладки в руках не остается ни одного источника доверия кроме даташита. И такая запись здорово экономит время при отладке поскольку соотносится с даташитом напрямую. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 117 30 июля, 2019 Опубликовано 30 июля, 2019 · Жалоба Тоже вброшу свой вариант: // Set PLLMUL & prescalers, select HSI48 clock as system clock RCC->CFGR = 0 | 0 * RCC_CFGR_PLLNODIV // PLL divider for MCO: 0: MCO = PLL/2, 1: MCO=PLL/1 | 0 * (RCC_CFGR_MCOPRE & -RCC_CFGR_MCOPRE) // MCO prescaler, 0: /1, 1: /2, 2: /4, 3: /8, 4: /16, // 5: /32, 6: /64, 7: /128 | 0 * (RCC_CFGR_MCOSEL & -RCC_CFGR_MCOSEL) // MCO source, 0: disabled, 1: HSI14, 2: LSI, 3: LSE, // 4: SYSCLK, 5: HSI8, 6: HSE, 7: PLL, 8: HSI48 | (PLL_MUL - 2) * (RCC_CFGR_PLLMUL & -RCC_CFGR_PLLMUL) // Fvco = Fpll_in / PREDIV * PLLMUL | 0 * RCC_CFGR_PLLXTPRE // LSB of PREDIV | 1 * (RCC_CFGR_PLLSRC & -RCC_CFGR_PLLSRC) // PLL source, F04x, F07x, F09x, 0: HSI/2, 1: HSI/PREDIV, 2: HSE/PREDIV, 3: HSI48/PREDIV | 0 * (RCC_CFGR_ADCPRE & -RCC_CFGR_ADCPRE) // ADC clock: obsolete, use ADC->CFGR2 settings | ( (AHBCLK / APBCLK == 1) ? 0 : (AHBCLK / APBCLK == 2) ? 4 : (AHBCLK / APBCLK == 4) ? 5 : (AHBCLK / APBCLK == 8) ? 6 : (AHBCLK / APBCLK == 16) ? 7 : (1 << 64) // unsupported value, generate uint32_t overflow warning ) * (RCC_CFGR_PPRE & -RCC_CFGR_PPRE) // APB prescaler, 0...3: /1, 4: /2, 5: /4, 6: /8, 7: /16 | ( (SYSCLK / AHBCLK == 1) ? 0 : (SYSCLK / AHBCLK == 2) ? 8 : (SYSCLK / AHBCLK == 4) ? 9 : (SYSCLK / AHBCLK == 8) ? 10 : (SYSCLK / AHBCLK == 16) ? 11 : (SYSCLK / AHBCLK == 64) ? 12 : (SYSCLK / AHBCLK == 128) ? 13 : (SYSCLK / AHBCLK == 256) ? 14 : (SYSCLK / AHBCLK == 512) ? 15 : (1 << 64) // unsupported value, generate uint32_t overflow warning ) * (RCC_CFGR_HPRE & -RCC_CFGR_HPRE) // AHB prescaler, 0...7: /1, 8: /2, 9: /4, 10: /8, 11: /16 // 12: /64, 13: /128, 14: /256, 15: /512 | 3 * (RCC_CFGR_SW & -RCC_CFGR_SW) // SYSCLK source: 0 = HSI, 1 = HSE, 2 = PLL, 3 = HSI48 ; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться