Jump to content

    
Sign in to follow this  
Arlleex

Атомарная запись нескольких битовых полей структуры в Си

Recommended Posts

Приветствую!

До этого момента описывал периферию МК как натянутую на нужные адреса регистровую модель этой самой периферии. Что-то типа

#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. Возможно ли такое средствами языка?

Share this post


Link to post
Share on other sites
59 minutes ago, Arlleex said:

Теперь я хочу одним махом каким-то образом сразу инициализировать поля M и N в структуре CR. Возможно ли такое средствами языка?

В принципе вполне возможно, но тогда придется руками описывать все эти поля. Имхо, очень трудоемко это.

 

 

Share this post


Link to post
Share on other sites
2 часа назад, Arlleex сказал:

Может есть какой-нибудь трюк, все-таки позволяющий атомарно записать несколько битовых полей структуры?

Если речь про Cortex-M, то конечно сеть: LDREX/STREX. А "средствами языка" - вряд-ли, потому что язык си не имеет отношения к системе команд - на одной платформе что-то будет атомарным, а на другой - оно уже может быть и не атомарным.

Share this post


Link to post
Share on other sites

Упс. Тут под атомарностью я имел ввиду не механизм доступа к памяти, а единое выражение Си-кода, не более:blush:

Вот пример. Есть структура, определяющая биты в регистре

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 всех битов и масок все ровно наоборот.

Share this post


Link to post
Share on other sites
3 минуты назад, Arlleex сказал:

в то же время нельзя одним махом хитро записать сразу в два поля этой структуры (используя только имена битовых полей, иначе фишка теряется). А у способа с #defines всех битов и масок все ровно наоборот.

Скопируйте в локальную переменную и присвойте в ней, а потом - обратно. Хотя сишных операций всё равно будет столько же, но количество команд компилятор возможно оптимизирует.

Share this post


Link to post
Share on other sites
7 minutes ago, Arlleex said:

Упс. Тут под атомарностью я имел ввиду не механизм доступа к памяти, а единое выражение Си-кода, не более:blush:

Единственный вариант (если уж так принципиально писать разом) - писать сразу весь регистр, БЕЗ битовых полей.

Принцип чтение-модификация-запись

 

Честно говоря, у меня тоже была похожая мысль - обращаться к полям регистров по вот таким 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 (я про файлы, где расписаны регистры, маски и флаги на соотв. семейство). Их поэтому не нужно объявлять.

 

 

Share this post


Link to post
Share on other sites
9 минут назад, jcxz сказал:

Скопируйте в локальную переменную и присвойте в ней, а потом - обратно. Хотя сишных операций всё равно будет столько же, но количество команд компилятор возможно оптимизирует.

Угу. Видимо, только так. Скорее всего, придется все-таки по старинке, с помощью #defines. Придумаю, как оптимально их обзывать, чтобы они никогда не повторялись в проекте.

 

8 минут назад, Forger сказал:

В данном случае эти макросы уже есть в каждом h-файле у тех же ST (я про файлы, где расписаны регистры, маски и флаги на соотв. семейство). Их поэтому не нужно объявлять.

Да это понятно... Но я все-таки отвожу себе времени на собственное описание периферии - оно и в голове лучше укладывается, и свой код потом приятно смотреть. А собственных макросов, облегчающих жизнь, у меня не меньше, чем дофига:wink:

 

Спасибо всем!

Share this post


Link to post
Share on other sites
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. Не вижу смысла уже тут - внутри моей библиотеки - делать какие-то особые красивости :)

Share this post


Link to post
Share on other sites
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
}

 

 

Функция собирается по частям под необходимые настройки проекта:wink: При этом вверху в .c-файле еще целый раздел по статической проверке корректности настроек - чтобы при использовании не задуть частоту CPU на диапазон PLL, например...

Share this post


Link to post
Share on other sites
Только что, Forger сказал:

"Не дописан" говорите? :biggrin: 

Да, совсем маленько - в закомментированном месте нужно настроить циклы ожидания доступа к Flash, ну и (возможно) каким-нибудь таймером следить за timeout-ом установки PLL Lock (сейчас, как видно, задержка на for()-е)...

Share this post


Link to post
Share on other sites
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 ... Впрочем, как и у других, кто затеял подобные дела )))

 

 

Share this post


Link to post
Share on other sites

Если в объявлении нет слов volatile, то компилятор может свернуть запись нескольких битовых полей в одно чтение-модификация-запись. Не проверяли?

Share this post


Link to post
Share on other sites
20 minutes ago, Arlleex said:

А вот мой инициализатор тактирования LPC1768 (правда еще не дописан чутка)

Функция собирается по частям под необходимые настройки проекта:wink: При этом вверху в .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

Потому что когда дело доходит до паранойи отладки в руках не остается ни одного источника доверия кроме даташита.
И такая запись здорово экономит время при отладке поскольку соотносится с даташитом напрямую. 

Share this post


Link to post
Share on other sites

Тоже вброшу свой вариант:


    // 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
        ;

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this