STM32 BSRR VS ODR.

STM32 BSRR VS ODR.

Те кто читает эту статью, думаю знают, что записать единицу в нулевой бит порта А можно двумя способами
GPIOA->ODR |= GPIO_ODR_ODR0;

или
GPIOA->BSRR  = GPIO_BSRR_BS0; 

И везде пишут, что второй способ правильнее так, как установка бита в таком случае выполянется атомарно. Тут не поспоришь.

А теперь давайте представим ситуацию, у нас есть восьмибитная шина, по которой передаются данные, подключение к шине выглядит следующим образом.
//D0 -  PD14
//D1 -  PD15
//D2 -  PD0
//D3 -  PD1

//D4 -  PE7
//D5 -  PE8
//D6 -  PE9
//D7 -  PE10

То есть получается, перед установкой очередного бита средствами BSRR, необходимо проверить его значение и в зависимости от того, что хотим получить, производить манипуляции. Но проверять каждый бит долго, поэтому предлагаю посмотреть на эту ситуацию под другим углом.

Линии относящиеся к порту Е : Е7, Е8, Е9, Е10 пронумерованы последовательно, а линии относящиеся к порту D образуют две последвательные пары PD0, PD1 и PD14, PD15.
Как писалось выше, с помощью BSRR устанавливать биты не вариант — очень долго. Это можно сделать быстрее, переписывая фрагменты регистра ODR, но теряя при этом атомарность.
И так первым делом из нашей переменной извлечём данные, которые будут передаваться по порту Е(D4 - D7).
cmd & 0xF0

В результате этой операции мы получили данные, которые надо передать, осталось их установить в нужную позицию. Сейчас данные располагаются с 4 по 7 бит, а должны с 7 по 10, поэтому сдвинем их на три позиции влево.
(cmd & 0x00F0)<<3;

В итоге получим следующее выражение
GPIOE->ODR |= (cmd & 0xF0)<<3;

С помощью тех же рассуждений записываем нужное значение в порт D.
GPIOD->ODR |= (cmd & 0x03)<<14;

и
GPIOD->ODR |= (cmd & 0x0C)>>2;

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


#define __enter_critical() 	{uint32_t flag; flag = __get_PRIMASK(); 
#define __exit_critical() 	__set_PRIMASK(flag);}
#define _Atomic(X) 		__enter_critical(); {X}; __exit_critical();

static void TFT_Send_Cmd(uint8_t cmd)
{
	_Atomic
	(
		RS_LOW
		RD_HIGH
		
		CS_LOW
		GPIOE->ODR &= ~(0xF0<<3);
		GPIOE->ODR |= (cmd & 0xF0)<<3;
		
		GPIOD->ODR &= ~(0x03<<14);
		GPIOD->ODR |= (cmd & 0x03)<<14;
		
		GPIOD->ODR &= ~(0x0C>>2);
		GPIOD->ODR |= (cmd & 0x0C)>>2;
		
		WR_LOW
		delay_us(5);
		WR_HIGH
		CS_HIGH
	)
}	

В этой ситуации использование регистра ODR оказалось оправданным, а вопрос атомарности решается, с помощью регистра PRIMASK, который будучи установлен в единицу запрещает все прерывания с конфигурируемым приоритетом. 
комментарии
5