Работа с регистрами контроллера STM32



Передача данных .

Передача данных инициируется записью передаваемых данных в регистр ввода/вывода данных USART. Данные пересылаются из регистра данных USARTx->DR в сдвиговый регистр передатчика, когда:

– новый символ записывается в регистр данных USARTx->DR после того, как был выдви­нут стоповый бит для предыдущего символа. При этом сдвиго­вый регистр загружается сразу;

– новый символ записывается в USARTx->DR до того, как выдвинут стоповый бит для предыдущего символа. Тогда в сдвиговый ре­гистр данные записывается не сразу, а только после того, как в линию будет выдвинут стоповый бит предыдущего символа.

Как только информация из USARTx->DR записывается в сдвиговый регистр, устанавливается флаг USART_FLAG_TXE – признак очистки регистра данных. Когда этот бит установлен, USART готов к приему следующего символа.

После тактового импульса, следующего с частотой передачи, стартовый бит выдвигается на вывод TX. Затем выдвигаются дан­ные, начиная с младшего бита. После того как выдвинут стоповый бит, в сдвиговый регистр загружаются новые данные, если они были записаны в USARTx->DR во время передачи. При загрузке устанавливается бит USART_FLAG_IDLE. Если до выдвижения стопового бита в регистр USARTx->DR не поступают новые данные, USART_FLAG_IDLE остается установленным до после­дующей записи USARTx->DR. Если новые данные не поступили и на выводе TX появляется стоповый бит, устанавливается флаг окончания передачи – USART_FLAG_TC.

Установка бита USART_FLAG_TXE разрешает работу передатчика, а сброс-запрещает. Однако выводы, которые занимает USART, можно будет использовать для ввода/выво­да данных только в том случае, если переконфигурировать их и снять отметку о том, что эти биты работают в альтернативном режиме.

 

Прием данных

Логическая схема приемника обрабатывает сигнал на выводе RX с частотой в 16 раз больше скорости передачи (для обработки одного бита принимаемой последовательности производится 16 вы­борок входного сигнала). В состоянии ожидания старт-бита одна выборка логи­ческого нуля интерпретируется как спадающий фронт стартового би­та, после чего запускается последовательность обнаружения старто­вого бита. Если в первой выборке сигнала обнаружен нулевой отсчет, приемник обрабатывает 8, 9 и 10 выборки сигнала на выводе RX. Если хотя бы две из трех выборок равны логической единице, стартовый бит считается шумом и приемник ждет следующего пере­хода из 1 в 0.

       Если обнаружен стартовый бит, начинается обработка битов данных. Решение об уровне данных также производится по 8, 9 и 10 выборкам входного сигнала, уровень входного сигнала определяется равенством двух из трех выборок. После того как уровень данных определен данные вдвигаются в сдвиговый регистр приемника.

       Для определения стопового бита хотя бы две из трех выборок входного сигнала должны быть равны 1. Если это условие не выполняется, устанавливается флаг ошибки кадра USART_FLAG_FE. Перед чтением данных из регистра USARTx->DR пользователь должен проверять бит USART_FLAG_FE для обнаружения ошибок кадра.

       Независимо от принятия правильного стопового бита по окончании приема кадра принятые данные переписываются в USARTx->DR, а также устанавливается флаг USART_FLAG_RXNE. Физически регистр USARTx->DR состоит из двух отдельных регистров, один используется для переда­чи данных, другой – для приема. При чтении USARTx->DR происходит до­ступ к регистру приемника, при записи – к регистру передатчика. При обмене 9-битовыми данными 9-й бит принятых данных записы­вается в бит RXB8 регистра UCR.

       Если после окончания приема символа в сдвиговый регистр из регистра USARTx->DR не был прочитан пре­дыдущий символ, устанавливается флаг переполне­ния – USART_FLAG_ORE. Установка этого флага означает, что последний принятый байт данных не переписывается из сдвигового регистра в регистр USARTx->DR и будет потерян. Флаг USART_FLAG_ORE буферирован и обновляется при чте­нии правильных данных из USARTx->DR. Таким образом, пользователь все­гда может проверить состояние USART_FLAG_ORE после чтения USARTx->DR и обнаружить произошедшее переполнение.

       При сбросе бита RXEN в регистре UCR прием данных запреща­ется. При этом вывод PD0 можно использовать для ввода/вывода об­щего назначения. При установке RXEN приемник подключен к вы­воду PD0 независимо от состояния бита в регистре DDRD.

 

STM32F4 содержит четыре USART, каждый из которых содержит до нескольких расширенных режимов для работ в составе современных устройств с последовательной передачей данных. Каждый из USART может обмениваться данными со скоростью до 5,25 Мбит/с, а первый до 10 Мбит/с. Для каждого USART можно задавать длину пересылки данных, стоповый бит четности и скорость передачи. Один USART расположен на шине APB2, а остальные на APB1.

Задающий генератор каждого USART представляет собой генератор дробных скоростей передачи, что намного сложнее, чем простой делитель частоты. Он позволяет получать стандартные скорости передачи из любой частоты шины. Как все остальные последовательные коммуникационные периферийные устройства, каждый USART поддерживает два канала DMA для передачи данных в память и из памяти. Когда USART используется как UART, он поддерживает несколько специальных режимов передачи данных.

В качестве примера рассмотрим двухстороннюю передачу информации по UART между микроконтроллером и персональным компьютером. Подключить STM32F4Discovery к компьютеру возможно несколькими способами: Самый универсальный способ с точки зрения современных представлений – использование контроллера USB-RS-232. Это может быть старый переходник BM8050, собранный на CP102 и MAX3243 или более современный адаптер на основе PL-2303MX.

В любом случае при подключении к компьютеру для них необходимо установить драйвер, который будет работать с контроллером USB-RS232 и добавит в операционной системе возможность доступа к виртуальному COM порту.

 

Работа с регистрами контроллера STM32

В контроллерах STM32 для настройки каждого модуля USART и работы с ними имеется по 7 регистров:

1. USART_SR регистр статуса — содержит флаги, указывающие на состояние модуля;

2. USART_DR регистр данных — в него пишем передаваемые данные и читаем принятые;

3. USART_BRR регистр, определяющий скорость обмена;

4. USART_CR1 управляющий регистр 1;

5. USART_CR2 управляющий регистр 2;

6. USART_CR3 управляющий регистр 3;

7. USART_GTPR регистр делителя и задержки.

 

Регистр статуса USART_SR. В нем находятся флаги, указывающие на текущее состояние модуля. Анализируя их, ЦПУ должен соответственно реагировать. Всего имеется 10 флагов, из них особо важными являются 6:

· PE (разряд 0) — ошибка паритета при приеме байта. Можно задавать четное или нечетное число единиц в принимаемом байте. Если приняли байт, а в нем другое количество единиц, возникает данная ошибка (аппаратная проверка на ошибки).

· FE (разряд 1) — ошибка кадра при приеме байта, т.е. первый стоп-бит принятой посылки равен «0″.

· NE (разряд 2) — флаг наличия шума. Ставится аппаратно, когда обнаружен шум в принимаемом кадре.

Данные флаги позволяют определить наличие ошибки при приеме байта (говорю байт, хотя передаваемая посылка может иметь разрядность отличную от 8). Очищаются они программно, последовательностью операций чтения регистра USART_SR, с последующим чтением регистра USART_DR.

Еще три флага позволяет определить окончание передачи или приема данных:

· RXNE (разряд 5) — в регистре данных приема есть данные, т.е. принят байт данных, нужно его обработать. Если в USART_CR1 бит RXNEIE=1, то генерируется прерывание. Этот бит очищается чтением регистра USART_DR (это согласно описанию, но у меня он очищался только записью в него нуля).

· TC (разряд 6) — передача завершена. Если регистр передачи данных пуст (на что указывает флаг TXE регистра USART_SR) и сдвиговый регистр тоже пуст. USART_CR1 бит TCIE=1, то генерируется прерывание. Очищается чтением USART_SR с последующей записью в USART_DR.

· TXE (разряд 7) — регистр данных передачи пуст. Очищается программно, записью в регистр USART_DR новых данных.

Отличие TXE от TC. USART передает данные из сдвигового регистра. В сдвиговый регистр данные попадают из регистра DR: процессор записывает данные только в DR и в нем они будут находиться пока сдвиговый регистр не освободится. Как только он освободится (данные будут переданы) информация из DR будет переписана в него и будет установлен флаг TXE (регистр данных пуст). У процессора есть время, равное времени выдвижения последнего байта в линию передачи. Если процессор за это время никак не отреагирует, то по освобождению сдвигового регистра будет установлен также флаг TC.

Регистр USART_DR содержит байт принятых или передаваемых данных. Хотя в программе обращение происходит как бы к одному регистру, на самом деле он состоит из двух - при чтении информация поступает из регистра приемника модуля, а при записи данные заносятся в регистр передатчика. Для работы с регистром данных используется следующие конструкции:

USART1->DR = data; //передать байт

tmp = USART1->DR; //прочитать принятый байт

Регистр USART_BRR определяет скорость обмена. Содержит переменную, определяющую скорость обмена. Эта переменная состоит из двух частей — основной и дробной. Благодаря дробной части можно весьма точно отстроить скорость обмена независимо от частоты тактового сигнала. Скорость обмена вычисляется по формуле:

baud = fck / (16*USARTDIV)

где fck — входной тактовый сигнал (PCLK1 для USART2, 3 и PCLK2 для USART1);
USARTDIV — число с фиксированной точкой, которым закодирован регистр USART_BRR.

Сответственно USARTDIV = fck / (16*baud)

Если нужна скорость 9600 при fck=8MHz : USARTDIV = 8000000/(16*9600)=52,0833

Округляем до двух разрядов после запятой и получаем USARTDIV =52,08

Что же загрузить в USART_BRR? Обратим внимание на его структуру:

· разряды 0..3 — DIV_Fraction, или дробная часть;

· разряды 4..15 — DIV_Mantissa, или основная часть.

Не спешите записывать 08 в DIV_Fraction.

08 (т.е. полученный остаток) необходимо умножить на 16.

0.08 * 16 = 1.28 -> окрулив до ближайшего целого числа получим DIV_Fraction = 0×01 (если бы получили 10, то в шестнадцатеричной системе имели бы ox0A).

Преобразовав основную часть в hex, имеем DIV_Mantissa = 0d52 = 0×34.

И, наконец, имеем значение регистра скорости передачи:

USART1->BRR = 0x0341 //записать значение скорости в BRR модуля USART1

Есть одна маленькая загвоздочка - мы выполнили округление : было 52,08(3), а получили 52,08. Какая погрешность? Вычислим ее. Для этого необходимо преобразовать полученное значение USART_BRR в USARTDIV:

целая часть = 0×34 = 0d52, дробная часть = 1/16 = 0.0625, USARTDIV = 52.0625 вместо первоначальных 52,0833.

Посчитаем ошибку настройки в процентах :

error = 100% - 52.0625*100 / 52,0833 = 0.04%.

Да, чуть не забыл. Если в результате вычислений получили DIV_Fraction после округлений равен 16, то необходимо выполнить перенос единицы в основную часть. Постараюсь позже написать утилитку, которая вычисляет значение USART_BRR по заданным fck и baud. :-)

Дальше рассмотрим три управляющих регистра USART_CR1, USART_CR2, USART_CR3.
Рассматривать будем только необходимые для нашей задачи разряды.

 

Регистр USART_CR1:

· UE (разряд 13) разрешение работы USART : 1 — работает; 0 — не работает;

· M (разряд 12) длина слова: 0 — 8 бит данных; 1 — 9 бит данных;

· PCE (разряд 10) разрешение контроля паритета: 0 — отключен; 1 -включен;

· PS (разряд 9) тип паритета: 0 — четный; 1 — нечетный;

· PEIE (8 разряд) разрешение прерывания при обнаружении ошибки паритета;

· TXEIE (разряд 7) разрешение прерывания от TXE (когда регистр передачи пуст);

· RXNEIE (разряд 6) разрешение прерывания от RXNE (в регистр данных перемещен принятый байт);

· TE (разряд 3) разрешить работу передатчика;

· RE (разряд 2) разрешить работу приемника;

 

Регистр USART_CR2:

STOP(разряды 13,12) формат стоп-битов: 00 — стоп-бит, 01 — 0.5 стоп-бита, 10 - 2 стоп-бита, 11 — 1.5 стоп-бита;

 

Регистр USART_CR3:

· DMAT (разряд 7) разрешить работу передатчика через DMA;

· DMAR (разряд 6) разрешить работу приемника через DMA;

На этом пока закончим рассматривать регистры.

Продолжим изучение усарта с рассмотрения примеров инициализации.

 

Управление UART

 

GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE); //разрешаем тактирование

//назначаем альтернативные функции
GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_USART2); //PD5 to TX USART2
GPIO_PinAFConfig(GPIOD, GPIO_PinSource6, GPIO_AF_USART2); //PD6 to RX USART2

//заполняем поля структуры
// PD5 -> TX UART.
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure); //инициализируем

//PD6 -> RX UART.
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_Init(GPIOD, &GPIO_InitStructure);//инициализируем

 

Настройка UART’а происходит подобным образом с использованием стандартной библиотеки. Первым делом не забываем подключить библиотечные файлы (stm32l1xx_usart.x).
Далее в файле stm32l1xx_conf.h раскоментим строчку #include «stm32l1xx_usart.h»
Затем смотрим в datasheet, отмечаем, что USART2 находится на шине APB1. Именно частота данной шины будет использоваться в качестве Fclk в формулах расчета битрейта. Если есть желание посчитать битрейт в ручную, необходимо сначала точно определить частоту данной шины, и иметь в виду, что при установленном бите OVER8 в регистре настроек, скорость работы UART'а вдвое выше.


Удобно, что стандартная библиотека избавляет нас от необходимости брать три листа бумаги А4, ручку, инженерный калькулятор, схему тактирования, настройки прескаллеров и аккуратно выплясывать танцы с бубнами вокруг расчетных формул самостоятельно рассчитывать значение регистра USART_BRR, при определении битрейта. Однако алгоритм, заложенный в стандартную библиотеку, опирается на заданные константы источников тактирования. По умолчанию предполагается, что частота внешнего кварца равна 8 Мгц. Если в проекте используется кварц другого номинала необходимо переопределить константу HSE_VALUE в файле stm32l1xx_conf.h.
В целом настройка UART выглядит следующим образом:

 

USART_InitTypeDef USART_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //Разрешаем тактирование

/* Стандартные настройки COM порта ПК----------------------------------*/
/* USART2 configured as follow:
- BaudRate = 9600 baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled
*/
USART_InitStructure.USART_BaudRate = 9600;// скорость
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //8 бит данных
USART_InitStructure.USART_StopBits = USART_StopBits_1; //один стоп бит
USART_InitStructure.USART_Parity = USART_Parity_No; //четность - нет
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // управлени потоком - нет
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // разрешаем прием и передачу

//USART_OverSampling8Cmd(ENABLE); //можно уменьшить частоту семплирования
//USART_OneBitMethodCmd(ENABLE); //можно уменьшить количество стробирований
//USART_HalfDuplexCmd (ENABLE); // можно выбрать полудуплексный режим.

USART_Init(USART2, &USART_InitStructure); //инизиализируем

 

USART_OverSampling8Cmd(ENABLE) позволяет уменьшить количество семплов и колчичество стробирований. Дело в том, что изначально на каждый принятый бит происходит 16 замеров уровня, из них для определения уровня выбираются 3 средних, остальные используются для контроля за ошибками уровня (см. рис.) По этой причине, скорость передачи данных не может быть быстрее чем 1/16 от частоты шины APB1.
Функция USART_OverSampling8Cmd(ENABLE) позволяет уменьшить количество семплирований вдвое, тем самым увеличить максимальную скорость передачи до 1/8. А функция USART_OneBitMethodCmd(ENABLE)позволяет уменьшить количество стробирований до 1. (5 семпл на рисунке) (см. рис.)

 

Функция USART_HalfDuplexCmd (ENABLE) переводит UART в полудуплексный режим, при котором передача осуществляется по одному проводу, подключенному к ноге (Tx) при этом пин Rx освобождается (см. рис.)

Тут надо подумать как именно настраивается пин Tx, я склонен думать, что как обычно: на выход, в режиме пуш-пул. Но нигде не подвернулась документация по этому поводу. Сам лично не проверял. Если кто из специалистов точно знает — пожалуйста подскажите.
Глядя на рисунок, так надо настроить в режиме Открытый сток с подтяжкой к плюсу.

Помимо данных настроек при работе в синхронном режиме USART, необходимо настроить параметры тактирования, а именно, по спадающему или возрастающему переднему или заднему фронту сигнала происходит стробирование, выводить ли последний импульс (см. рис.)

 

USART_ClockInitTypeDef USART_ClockInitStructure;
USART_ClockInitStructure.USART_Clock=USART_Clock_Enable;
USART_ClockInitStructure.USART_CPHA=USART_CPHA_1Edge;
USART_ClockInitStructure.USART_CPOL=USART_CPOL_High;
USART_ClockInitStructure.USART_LastBit=USART_LastBit_Enable;
USART_ClockInit(USART2, &USART_ClockInitStructure);

 

Стоит отметить, что после настройки периферии, т.е. после выполнения пунктов 1-4 из раздела 2, можно начинать работу с UART’ом без использования прерываний (разрешив его использование командой USART_Cmd(USART2, ENABLE)). Так, например, сделано в некоторых из приведенных в самом начале статьи примеров. В данном случае отправка байта сводиться к заполнению регистра USART2->DRданными, предварительно проверив, завершена ли предыдущая передача.
Пример

 

while(!(USART2->SR & USART_SR_TC)); //Проверка завершения передачи предыдущих данных
USART2->DR = data; //Передача данных

 

или с использованием стандартной библиотеки командами

while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
USART_SendData(USART2,data);

 

Прием данных осуществляется аналогично: считыванием данных из регистра USART2->DR, предварительно проверив, пришли ли данные.

 


while(!(USART2->SR & USART_SR_RXNE)); //Ждем поступления данных от компьютера
data = USART2->DR; //считываем данные

 


или с использованием стандартной библиотеки командами

 


while (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET);
data = USART_ReceiveData(USART2);


Не нужно пугаться, что мы и пишем и читаем из одного и того же регистра USART2->DR. На самом деле за одним именем скрываются два различных регистра, поэтому чтение и запись происходят в разных местах и не влияют на данные друг друга.

Такой способ отправки и приема очень прост в реализации, но имеет существенный недостаток: необходимость ожидания установления соответствующего флага.
Для того чтобы избавиться от длительного ожидания факта поступления/передачи данных используют прерывания

 

Настройка прерываний.


Настройку прерываний также произведем с использованием стандартной библиотеки.
Первым делом не забываем подключить библиотечные файлы (misc.x).
Далее в файле stm32l1xx_conf.h раскоментим строчку #include «misc.h»
Настройка выглядит следующим образом

 


NVIC_InitTypeDef NVIC_InitStructure;
/* Configure the Priority Group to 2 bits */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //конфигурируем количество групп и подгрупп прерываний, пока у нас одно прерывание нам это особо ничего не дает

/* Enable the USARTx Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; //прерывание по uart2
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //задаем приоритет в группе
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //задаем приоритет в подгруппе
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //разрешаем прерывание
NVIC_Init(&NVIC_InitStructure); //инициализируем

 

Данным участком программы была разрешена генерация прерываний от UART’а в принципе, в частности же, прерывания от UART’а могут генерироваться по факту наступления одного из событий:


· USART_IT_CTS: Прерывание по изменению состояния CTS

· USART_IT_LBD: LIN Break detection interrupt.

· USART_IT_TXE: Прерывание по опустошению регистра передачи

· USART_IT_TC: Прерывание по окончанию передачи.

· USART_IT_RXNE: Прерывание по факту приема данных.

· USART_IT_IDLE: Idle line detection interrupt.

· USART_IT_PE: Прерывание по факту ошибки четности.

· USART_IT_ERR: Прерывание по факту ошибки (Frame error, noise error, overrun error).

 

Необходимо разрешить одно из этих событий. Разрешим генерировать прерывание по приему и передачи

USART_ITConfig(USART2, USART_IT_RXNE| USART_IT_TXE, ENABLE);

UPD:
Спасибо комментариям, так делать нельзя, правильно

 


USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART2, USART_IT_TXE, ENABLE);


Стоит отметить отличие между флагами USART_IT_TXE и USART_IT_TС. Первый срабатывает, как только освободился буфер и мы можем смело кидать еще данных, второй — когда передача полностью завершилась и мы можем смело отключать UART.

 

На последок разрешим использовать UART.

 


USART_Cmd(USART2, ENABLE);


С настройкой прерываний все. Осталось написать обработчики прерываний.



Дата добавления: 2021-12-10; просмотров: 31; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!