Перейти к содержимому

Заметки

Битовые операции

Операция сдвига

Для числа 1 (Dec, в десятичной системе 1) — 00000001 (Bin, в двоичной системе 0b00000001):

1 << 0 = 1 (00000001);
1 << 1 = 2 (00000010);
1 << 2 = 4 (00000100)
1 << 5 = 32 (00100000);
1 >> 2 = 0 (00000000).

Операция конъюнкция «&» (логическое И, AND) :

1101 0001 (209)
& 
0000 0111 (7)
----------------
0000 0001 (1) 

Операция дизъюнкция «|» (логическое ИЛИ, OR):

1101 0001 (209)
|
0000 0111 (7)
----------------
1101 0111 (215) 

Операция инверсия «~» (логическое НЕ):

1101 0001(209)
~
----------------
0010 1110(46) 

Применение битовых операций.

  1. Установка бита в регистре.

Например для включения тактирования на порту А нужно установить 3 бит в регистр APB2ENR можно использовать любую из следующих конструкций операторов, все инструкции выполняют идентичную задачу:

RCC->APB2ENR = RCC->APB2ENR | 4;
RCC->APB2ENR = RCC->APB2ENR | (1 << 2);
RCC->APB2ENR |= 0x000004;
RCC->APB2ENR |= (1 << 2);

Чтобы установить несколько бит в регистре нужно использовать битовую операцию «или» например установка 3 и 4 бита одной инструкцией:

RCC->APB2ENR |= ( 1 << 2 ) | ( 1 << 3 );

2. Сброс битов в регистре.

Для сброса разрядов в регистре необходимо использовать битовую операцию «&» (логическое «И»), которая применяется к двум битам (бинарная операция) и даёт единицу только в том случае если оба исходных бита имеют единичное значение, также необходима битовая операция «~» (логическое «НЕ», инверсия).

Для того чтобы сбросить например 5-й бит (10011101) в регистре нужно подготовить маску (как при установке битов), инвертировать ее биты «~», а потом выполним битовую операцию «&» над текущим значением регистра и полученной инвертированной маской.

Для подготовки маски выполним сдвиг битов на 4 разрядов в числе 1 (00000001).

0000 0001 (1)
<< 4
----------------
0001 0000 (16)

Маска готова, получили число 16 (00010000), 2 в 4-й степени. Выполним инверсию битов:

0001 0000 (16)
~ 
----------------
1110 1111 (239)

Готово, осталось применить маску к содержимому регистра используя битовую операцию «&»:

1001 1101 (157)
&
1110 1111 (239) 
----------------
1000 1101 (141)

В языке Си данные операции можно выполнить используя любую из приведенных ниже, идентичных по результату команд, например ниже приведена команда сброса флага окончания преобразования АЦП end of conversion:

ADC1->SR = ADC1->SR &~ 0x02;
ADC1->SR = ADC1->SR & ~( 1 << 1 );
ADC1->SR &= ~( 1 << 1 );

Для одновременного сброса нескольких битов регистра нужно использовать битовую операцию «или» . Сброс 2 и 3 бита:

ADC1->SR &= ~(1 << 1)|(1 << 2);


Чтобы, записать или считать какое значение в, из ячейки памяти по адресу 0x40010800 в языке СИ,используется указатель «*».

*((uint32_t*)0x40010800)

Теперь напишем программу pwm на портах

Прежде чем приступить к написанию программы для микроконтроллера, нужно подключить тактирование нужных блоков, за это отвечают регистры RCC — reset and clock control. Описание работы и их расположение можно узнать из reference manual к данной серии контроллеров https://www.st.com/content/ccc/resource/technical/document/reference_manual/59/b9/ba/7f/11/af/43/d5/CD00171190.pdf/files/CD00171190.pdf/jcr:content/translations/en.CD00171190.pdf. Затем нужно найти регистры которые включают тактирование.


Нам нужно включить тактирование на таймере 2 и на порту GPIOA.

Как видим для этого нужно установить 2 бит в регистре APB2ENR и 0 бит в регистре APB1ENR. Теперь осталось определить адреса этих регистров чтобы обратиться к ним. В refernce manual заходим в раздел memory map

Там находим адрес RCC Reset and clock control регистров, 0x40021000 теперь осталось определить смещение APB2ENR и APB1ENR регистров.


Соответственно регистр APB2ENR имеет адрес 0x40021018, а APB1ENR по адресу 0x4002101C. Функция установки битов будет иметь следующий вид.

void RCC_ENABLE(void)
{
    *((uint32_t*)0x40021018)|= 1 << 2;
    *((uint32_t*)0x4002101C)|= 1 << 0;
}

При запуске в режиме отладки в keil мы введя в поле memory адрес регистров увидеть как он изменяется 0x04=b0100

Каждый порт GPIO (GPIOA, GPIOB и т. д.) имеет 7 регистров:

2 используются для индивидуальной настройки шестнадцати битов порта,

2 — для параллельного чтения / записи шестнадцати битов порта,

2 — для индивидуальной установки / сброса шестнадцати битов порта,

1 — для реализации «последовательности блокировки», которая предназначена для предотвращения случайного изменения мошенническим кодом конфигурации порта. Эта последняя функция может помочь минимизировать вероятность того, что программные ошибки приводят к аппаратным сбоям; например, случайно вызвав короткое замыкание.

Структура регистров портов GPIO:

typedef struct
{
volatile uint32_t CRL;
volatile uint32_t CRH;
volatile uint32_t IDR;
volatile uint32_t ODR;
volatile uint32_t BSRR;
volatile uint32_t BRR;
volatile uint32_t LCKR;
} GPIO_TypeDef;

Адреса регистров различных портов в библиотеке stm32f10x.h:

#define PERIPH_BASE ((uint32_t)0x40000000)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)

Подмножество процедур, доступных для управления портами GPIO:

void GPIO_Init(GPIO_TypeDef* GPIOx ,
GPIO_InitTypeDef* GPIO_InitStruct);
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx ,
uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx ,
uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
void GPIO_SetBits(GPIO_TypeDef* GPIOx , uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx , uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx , uint16_t GPIO_Pin ,
BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx , uint16_t PortVal);

Адреса периферийных устройств:

0x40013800 - 0x40013BFF USART1
0x40013000 - 0x400133FF SPI1
0x40012C00 - 0x40012FFF TIM1 timer
0x40012400 - 0x400127FF ADC1 

Основные этапы инициализации, необходимые для использования любого из периферийных устройств STM32:

  1. Включить тактирование на периферию
  2. Настроить контакты, необходимые для периферийных устройств
  3. Настроить периферийное оборудование
#include <stm32f10x.h>
#include <stm32f10x_rcc.h>
#include <stm32f10x_gpio.h>
void Delay(uint32_t nTime);
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// Enable Peripheral Clocks
... (1) ...
// Configure Pins
... (2) ...
// Configure SysTick Timer
... (3) ...
while (1) {
static int ledval = 0;
// toggle led
... (4) ...
Delay(250); // wait 250ms
}
}
// Timer code
... (5) ...
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
/* Infinite loop */
/* Use GDB to find out why we're here */
while (1);
}
#endif