Jump to content

    

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

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

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

#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
1 minute ago, Arlleex said:

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

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

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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now