Наш магазин на eBay Наш магазин на AliExpress Наш канал в telegram

Программирование ARM-контроллеров STM32 на ядре Cortex-M3. Часть 12. Работа с модулями USART и UART.

Общее описание

USART — это модуль универсального синхронно-асинхронного приёмо-передатчика. В первую очередь эти модули используются для организации связи по интерфейсу RS-232 и другим, имеющим аналогичный формат фреймов (RS-422, RS-485). Модули, естественно, реализуют не весь интерфейс, а только нижний уровень логики (формат передачи одного фрейма, байта), к которому потом ещё нужно прикрутить правильную физику и, возможно, протокол более высокого уровня, но это не суть.

Кроме описанных выше, модули USART позволяют реализовать интерфейсы IrDA (обмен данными по ИК-каналу), LIN (используется в автомобилях совместно с CAN), SmartCard (ISO7816-3: чип-карты, симки и так далее).

Всего в контроллерах stm32 может быть до 3-х модулей USART (USART1, USART2, USART3) и до 2-х модулей UART (UART4, UART5) в зависимости от конкретной модели.

Модули UART предоставляют практически те же самые возможности, что и USART за исключением возможностей синхронного обмена данными и аппаратного контроля передачи (соответственно, для этих модулей недоступен режим Smartcard). Кроме того, для модуля UART5 недоступна работа через DMA.

Таблица возможностей различных модулей USART:

Таблица возможностей модулей USART контроллеров stm32

Физически выводы USART мультиплексированы с выводами GPIO. Для того, чтобы контроллер понял, что вы хотите использовать эти выводы именно в качестве выводов USART, — они должны быть специальным образом настроены в регистрах настройки GPIO:

Настройка выводов контроллера stm32 для работы с USART

Кроме того, с помощью регистров настройки AFIO можно «сремапить» (перенести) выводы некоторых модулей USART на другие ноги контроллера. В таблице ниже указано какие ноги используются модулями USART по умолчанию и куда их можно сремапить:

Выводы, используемые модулями USART контроллеров stm32

Управление модулями осуществляется через специальные регистры. Подробно эти регистры описаны в Reference manual RM0008, но коротенько я на них всё равно остановлюсь.

Регистры

USART_SR — регистр статуса. Младшие 10 бит этого регистра позволяют отслеживать состояние и различные события модуля USART. По каждому из этих событий может быть сгенерировано прерывание (настройка прерываний осуществляется в регистрах USART_CRx).

регистр USART_SR

  • CTS: устанавливается в 1 при изменении состояния линии CTS, если это разрешено настройками. Сбрасывается программно, записью в него нуля.
  • LBD: устанавливается в 1 при обнаружении сигнала Break для интерфейса LIN. Сбрасывается программно, записью в него нуля. По этому событию может генерироваться прерывание.
  • TXE (transmit data register empty): устанавливается в 1 после того, как все биты регистра TDR будут переданы в сдвиговый регистр передатчика. Сбрасывается автоматически при записи в регистр USART_DR.
  • TC (transmission complete): устанавливается в 1 после того как будут переданы все биты из сдвигового регистра передатчика, если при этом установлен бит TXE (то есть если в регистре данных передатчика нет новых данных для передачи). Сбрасывается автоматически, выполнением программной последовательности: чтение регистра USART_SR с последующей записью в регистр USART_DR. Также можно сбросить записью в этот бит нуля.
  • RXNE (read data register not empty): устанавливается в 1 после того, как очередная порция принятых данных будет перемещена из сдвигового регистра приёмника в регистр данных приёмника. Сбрасывается автоматически при чтении регистра USART_DR. Также можно сбросить записью в этот бит нуля.
  • IDLE: устанавливается в 1 после обнаружения на линии сигнала idle. Сбрасывается автоматически, выполнением программной последовательности: чтение регистра USART_SR с последующим чтением регистра USART_DR.
  • ORE (overrun error): устанавливается в 1, если в сдвиговом регистре приёмника готова очередная порция данных для передачи в регистр данных приёмника, но при этом из регистра данных приёмника ещё не считали предыдущие принятые данные. При возникновении этой ошибки данные в регистре RDR сохраняются, а в сдвиговом регистре приёмника перезаписываются.
  • NE (noise error): устанавливается в 1, если в принятом фрейме были обнаружены помехи (результаты нескольких выборок для какого-либо бита не совпали).
  • FE (framing error): устанавливается в 1 при обнаружении рассинхронизации (например, если приёмник в нужное время не обнаружил ожидаемые стоповые биты).
  • PE: устанавливается в единицу при обнаружении ошибки чётности.

Все биты ошибок (ORE, NE, FE, PE) сбрасываются автоматически при выполнении программной последовательности: чтение регистра USART_SR с последующим чтением регистра USART_DR.

USART_DR — регистр данных. Младшие 9 бит этого регистра используются для доступа к регистрам данных приёмника и передатчика. Фактически регистры для приёма и передачи — это разные регистры (TDR и RDR), но к ним нет прямого доступа, доступ к обоим этим регистрам осуществляется через USART_DR. Когда мы читаем данные из регистра USART_DR, то фактически они читаются из регистра RDR, а когда пишем данные в USART_DR, то фактически они пишутся в регистр TDR.

регистр USART_DR

USART_BRR — регистр настройки скорости передачи.

формула для вычисления USART_BRR

Младшие 4 бита регистра USART_BRR называются DIV_Fraction и содержат информацию о дробной части значения USARTDIV. Биты [4:15] регистра USART_BRR называются DIV_Mantissa и содержат информацию о целой части значения USARTDIV. Правила тут следующие: в DIV_Mantissa записывается целая часть значения USARTDIV, а в DIV_Fraction — умноженная на 16 и округлённая до ближайшего целого дробная часть USARTDIV. Если полученное после округления число занимает больше 4 бит, то старший бит добавляется к целой части, а в DIV_Fraction записываются только младшие 4 бита.

регистр USART_BRR

Например, при вычислениях получилось, что USARTDIV = 0d50.99, то есть целая часть = 0d50 = 0x32, а умноженная на 16 дробная часть = 0d15.84. При округлении до целого умноженной на 16 дробной части получаем 0d16 = 0x10. Число 0x10 занимает больше 4-х бит, следовательно DIV_Fractional = 0, а старшая единица добавляется к целой части, которая получается равной 0d50 + 1 = 0d51 = 0x33. Соответственно в USART_BRR нужно записать значение 0x330.

USART_СR1 — регистр настроек.

регистр USART_CR1

  • UE (usart enable): 1 — включение модуля USART, 0 — выключение.
  • M: определяет количество информационных битов в посылке (0 — 8 бит, 1 — 9 бит).
  • WAKE: метод пробуждения из режима ожидания (0 — по сигналу IDLE, 1 — по приёму спецслова, адреса).
  • PCE (parity control enable): 1 — включить контроль чётности, 0 — выключить.
  • PS (parity selection): 1 — проверяется нечётность, 0 — проверяется чётность.
  • PEIE: 1 — включить прерывание по ошибке чётности (флаг PE регистра статуса), 0 — выключить.
  • TXEIE: 1 — включить прерывание по опустошению регистра передатчика (флаг TXE регистра статуса), 0 — выключить.
  • TCIE: 1 — включить прерывание по завершению передачи (флаг TC регистра статуса), 0 — выключить.
  • RXNEIE: 1 — включить прерывание по приёму данных (флаги ORE или RXNE регистра статуса).
  • IDLEIE: 1 — включить прерывание по обнаружению на линии сигнала IDLE (флаг IDLE регистра статуса).
  • TE (transmitter enable): 1 — включение передатчика, 0 — выключение.
  • RE (receiver enable): 1 — включение приёмника, 0 — выключение.
  • RWU: 1 — переключение приёмника в режим ожидания, 0 — активный режим.
  • SBK: 1 — послать в линию сигнал break. Сбрасывается автоматически во время передачи стоповых битов сигнала break.

USART_СR2 — регистр настроек.

регистр USART_CR2

  • LINEN: 1 — включение режима LIN, 0 — выключение.
  • STOP[1:0]: определяет количество стоповых битов (00 — 1, 01 — 0.5, 10 — 2, 11 — 1.5).
  • CLKEN (clock enable): 1 — включить пин CK, 0 — выключить.
  • CPOL (clock polarity): определяет уровень сигнала на линии CK в отсутствии импульсов (0 — низкий, 1 — высокий).
  • CPHA (clock phase): определяет по которому фронту импульсов на линии CK происходит захват бита с линии данных (0 — по переднему, 1 — по заднему).
  • LBCL (last bit clock pulse): позволяет выбрать нужно ли тактировать через линию CK последний передаваемый бит (0 — не нужно, 1 — нужно).
  • LBDIE: 1 — включить прерывание по обнаружению сигнала break (в режиме LIN), 0 — выключить.
  • LBDL: определяет длительность ожидаемого сигнала break (0 — 10 бит, 1 — 11 бит).
  • ADD[3:0]: адрес узла USART. Здесь задаётся адрес текущего узла. Получив этот адрес приёмник выйдет из режима ожидания (если выбран соответствующий метод пробуждения).

USART_СR3 — регистр настроек.

регистр USART_CR3

  • CTSIE (CTS interrupt enable): 1 — включение прерывания по линии CTS (флаг CTS в регистре статуса), 0 — выключение.
  • CTSE (CTS enable): включение контроля линии CTS (данные начнут передаваться только когда на линии CTS установится низкий уровень).
  • RTSE (RTS enable): включение контроля линии RTS (на линии RTS выставляется низкий уровень только когда регистр приёмника пуст и готов к приёму).
  • DMAT (DMA enable transmitter): 1 — включить режим DMA для передатчика, 0 — выключить.
  • DMAR (DMA enable receiver): 1 — включить режим DMA для приёмника, 0 — выключить.
  • SCEN: 1 — включение режима Smartcard, 0 — выключение.
  • NACK: оперделяет посылать или нет NACK в режиме Smartcard при ошибке чётности (1 — посылать, 0 — не посылать).
  • HDSEL: 1 — включить однопроводный полудуплексный режим, 0 — выключить.
  • IRLP: 1 — включение режима low-power для IrDA, 0 — IrDA в режиме normal.
  • IREN (IrDA enable): 1 — включение режима IrDA, 0 — выключение.
  • EIE (error interrupt enable): 1 — включение прерывание по ошибкам передачи (флаги FE, ORE, NE регистра статуса), 0 — выключение.

Есть ещё регистр USART_GTPR, но в нём прописываются настройки только для режимов Smartcard и IrDA, так что про него сами в доке почитаете.

Техника программирования

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

Конфигурирование и настройка:

  • Включить тактирование USARTx, GPIO, AFIO. Выполняется в регистрах настройки RCC.
  • Если нужно, то сремапить выводы USART на другие пины. Выполняется в регистрах настройки AFIO.
  • Перевести используемые для USART выводы в нужные режимы. Как должны быть настроены выводы можно посмотреть в таблице 24 документа RM0008 (или вверху этой статьи, вторая картинка). Выполняется в регистрах настройки GPIO.
  • Настроить USART (скорость, режим, количество информационных и стоповых битов). Выполняется в регистрах настройки USART (там же сразу нужно выбрать, должен ли быть включен передатчик, приёмник или и то и другое).
  • Включить модуль USART. Выполняется в регистре USART_CR1.

Кроме того:

  • Если предполагается использовать прерывание от модуля USART, то в регистрах настройки USART необходимо разрешить прерывание от нужных вам событий. Помимо этого, если необходимо, то нужно настроить в регистрах NVIC_IPRx приоритет для прерывания от UART. И, наконец, в регистрах NVIC_ISERx нужно включить само прерывание.

    Учтите, что для каждого модуля USART генерируется всего одно прерывание. Далее нужно самостоятельно (по флагам регистра статуса) определять какое именно событие его вызвало.

  • Если для передатчика или приёмника будет использоваться DMA, то нужно включить для них режим DMA в регистре USART_CR3, а также настроить сам модуль DMA.

Отправка данных без использования DMA:

  • Убеждаемся, что бит TXE установлен (регистр передатчика пуст и в него можно писать данные).
  • Пишем данные для отправки в регистр USART_DR (это автоматически сбрасывает флаг TXE).
  • Анализируем флаг TXE (как только он снова взведётся — можно писать в регистр данных передатчика новый байт).
  • После записи последнего байта ждём пока последовательно установятся флаги TXE и TC в регистре статуса, что является признаком окончания передачи (последний байт ушёл из регистра данных передатчика и далее из сдвигового регистра передатчика).

Отправка данных с использованием DMA:

  • Заполняем буфер для передачи.
  • Включаем канал DMA, к которому подключен передатчик USART. Контроллер DMA будет сам анализировать флаг TXE и пихать очередной байт в регистр данных передатчика как только он освободится.
  • Ждём событие Transfer Complete для канала DMA (которое означает, что последний байт буфера был передан в регистр данных передатчика), после чего ждём последовательной установки флагов TXE и TC (последний байт ушёл из регистра данных передатчика и далее из сдвигового регистра передатчика).

Если для используемого канала DMA выбран режим Normal, то при каждом использовании DMA взводить счётчик переданных байт нужно вручную. Для этого выключают канал DMA, взводят счётчик (пишут в него количество байт для передачи) и снова включают канал DMA.

Если же для используемого канала DMA выбран режим Сircular, то после каждого обнуления счётчика он будет автоматически инициализироваться исходным значением. Соответственно, тут же начнётся передача новой порции данных. И так до тех пор, пока канал DMA не будет выключен.

Приём данных без использования DMA:

  • Анализируем бит RXNE в регистре статус. Его установка означает, что из сдвигового регистра приёмника в регистр данных приёмника был вытеснен очередной байт и его можно забирать.
  • Далее нужно прочитать и проанализировать регистр статуса на наличие ошибок, а также успеть забрать принятый байт до того, как придёт следующий. Чтение регистра данных приёмника автоматически очищает бит RXNE.

Приём данных с использованием DMA:

  • Включаем канал DMA, к которому подключен приёмник USART. С этого момента контроллер DMA будет сам анализировать флаг RXNE и при наличии принятого байта вычитывать его из регистра данных приёмника в буфер (что автоматически будет сбрасывать флаг RXNE).
  • Событие Transfer Complete для канала DMA в случае приёма будет означать, что приёмный буфер целиком заполнен. Но нам всё равно нужно после каждого принятого байта анализировать регистр статуса на наличие ошибок. Это можно сделать, установив прерывание по событию RXNE или просто включив прерывание по ошибкам.

Пример

В качестве примера рассмотрим программу, которая будет принимать байты в буфер через USART, и отправлять этот буфер обратно после того, как в нём накопится 5 байт.

Текст программы

#include "stm32f10x.h" /* стандартные хидеры */
 
/* сначала всякие переменные (ну а как вы хотели с языками высокого уровня) */
GPIO_InitTypeDef PORT_Structure;	/* структура для настройки линий порта */
USART_InitTypeDef USART_Structure;	/* структура для инициализации USART */
NVIC_InitTypeDef NVIC_Structure;	/* структура для настройки NVIC */
DMA_InitTypeDef DMA_Structure;		/* структура для настройки DMA */
 
uint8_t USART_Value[5];			/* буфер для USART */
uint8_t temp;				/* счётчик полученных байт */
 
/* Прототипы функций */
void GPIO_Configuration(void);		/* функция настройки GPIO */
void DMA_Configuration(void);		/* функция настройки DMA */
void USART_Configuration(void);		/* функция настройки USART */
void NVIC_Configuration(void);		/* функция настройки NVIC */
 
/* Основная программа */
int main(void) {
 
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	/* включаем тактирование DMA */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 |
				RCC_APB2Periph_GPIOA |
				RCC_APB2Periph_AFIO, ENABLE);	/* включаем тактирование USART1, GPIOA, AFIO */
	temp = 0;		/* обнуляем счётчик */
 
	GPIO_Configuration();		/* настраиваем GPIO */
	DMA_Configuration();		/* настраиваем DMA */
	USART_Configuration();		/* настраиваем USART */
	NVIC_Configuration();		/* настраиваем NVIC */
 
Work:
	goto Work;
}
 
 
/* Настройка GPIO */
void GPIO_Configuration(void)
{	/* Configure USART1 Tx (PA.09) as alternate function push-pull */
	PORT_Structure.GPIO_Pin = GPIO_Pin_9;			/* пин 9, сюда подключена линия Tx модуля USART */
	PORT_Structure.GPIO_Mode = GPIO_Mode_AF_PP;		/* альтернативная функция push-pull */
	PORT_Structure.GPIO_Speed = GPIO_Speed_50MHz;		/* максимальная скорость 50 МГц */
	GPIO_Init(GPIOA, &PORT_Structure);
 
	/* Configure USART1 Rx (PA.10) as input floating */
	PORT_Structure.GPIO_Pin = GPIO_Pin_10;			/* пин 10, сюда подключена линия Rx модуля USART */
	PORT_Structure.GPIO_Mode = GPIO_Mode_IN_FLOATING;	/* аналоговый вход */
	/* максимальная скорость не меняется, так что заново этот элемент структуры можно не записывать */
	GPIO_Init(GPIOA, &PORT_Structure);
}
 
void DMA_Configuration(void)
{	/* инициализируем четвёртый канал DMA1 (к этому каналу подключен передатчик USART1) */
	DMA_Structure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);
	DMA_Structure.DMA_MemoryBaseAddr = (uint32_t)&USART_Value;
	DMA_Structure.DMA_DIR = DMA_DIR_PeripheralDST;		/* направление из памяти в периферию */
	DMA_Structure.DMA_BufferSize = 5;
	DMA_Structure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_Structure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_Structure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_Structure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_Structure.DMA_Mode = DMA_Mode_Normal;		/* после отправки всех данных нужно взводить счётчик вручную */
	DMA_Structure.DMA_Priority = DMA_Priority_Low;
	DMA_Structure.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA1_Channel4, &DMA_Structure);
}
 
void USART_Configuration(void)
{	/* инициализируем USART */
	USART_Structure.USART_BaudRate = 115200;					/* BaudRate = 115200 */
	USART_Structure.USART_WordLength = USART_WordLength_8b;				/* Word Length = 8 Bits */
	USART_Structure.USART_StopBits = USART_StopBits_1;				/* One Stop Bit */
	USART_Structure.USART_Parity = USART_Parity_No;					/* No parity */
	USART_Structure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	/* Hardware flow control disabled */
	USART_Structure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;			/* Receive and transmit enabled */
	USART_Init(USART1, &USART_Structure);
 
	USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);	/* включаем режим DMA для передатчика USART */
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);	/* включаем прерывание по приёму очередного байта */
	USART_Cmd(USART1, ENABLE);			/* включаем USART */
}
 
void NVIC_Configuration(void)
{	/* включаем прерывание для USART1 */
	NVIC_Structure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_Structure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_Structure.NVIC_IRQChannelSubPriority = 0;
	NVIC_Structure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_Structure);
}
 
/* обработчик прерывания от USART1 */
void USART1_IRQHandler(void)
{	/* у нас для UART1 включено прерывание только по одному событию, поэтому */
	/* можно не выяснять что конкретно случилось, а сразу приступать к чтению */
	/* бит RXNE сбросится автоматически при чтении байта */
	USART_Value[temp] = USART_ReceiveData(USART1);	/* сохраняем принятый байт в буфер */
	temp = temp+1;
	if(temp == 5)							/* если приняли 5 байт */
	{	temp = 0;						/* обнуляем счётчик */
		DMA_Cmd(DMA1_Channel4, DISABLE);			/* выключаем четвёртый канал DMA1 */
		DMA1_Channel4->CNDTR = 5;				/* взводим счётчик */
		DMA_Cmd(DMA1_Channel4, ENABLE);				/* включаем четвёртый канал DMA1 */
	}
}

[свернуть]
  1. Часть 1. Установка MDK, создание проекта, основы Keil uVision
  2. Часть 2. Команды и директивы ассемблера, структура и синтаксис программы. Первая программа для STM32
  3. Часть 3. Карта памяти контроллеров STM32, методы работы с памятью
  4. Часть 4. Регистры, старт и режимы работы контроллеров STM32
  5. Часть 5. Как залить прошивку в контроллер
  6. Часть 6. Настройка системы тактирования
  7. Часть 7. Работа с портами ввода-вывода
  8. Часть 8. Процедуры на ассемблере для STM32
  9. Часть 9. Система прерываний
  10. Часть 10. CMSIS, использование стандартных библиотек и функций
  11. Часть 11. Подключение и использование драйверов из пакета StdPeriph
  12. Часть 12. Работа с модулями USART и UART.
  13. Часть 13. Работа с модулями ADC
  14. Часть 14. Использование DMA
  15. Часть 15. Таймеры. Глава 1 — Введение. Простейшие таймеры
  16. Часть 15. Таймеры. Глава 2 — Таймеры общего назначения TIM9 — TIM14
  17. Часть 15. Таймеры. Глава 3 — Таймеры общего назначения TIM2 — TIM5
  18. Часть 15. Таймеры. Глава 4 — Продвинутые таймеры TIM1, TIM8
  19. Часть 16. Создание устройства USB HID в Keil uVision при помощи библиотечного компонента USB
  20. Приложение 1. Набор инструкций THUMB-2 и особенности их использования
  21. Приложение 2. Таблица векторов прерываний для семейств STM32F101, STM32F102, STM32F103
  22. Приложение 3. Драйвера и функции библиотеки StdPeriph

Комментарии 25

  • Вы самни пробовали по своей формуле считать?
    // а старшая единица добавляется к целой части, которая получается равной 0d50 + 1 = 0d51 = 0x33.
    //Соответственно в USART_BRR нужно записать значение 0x330.
    Этот вот этап: //Соответственно в USART_BRR нужно записать значение 0x330.
    Вообще не описан никак. По логике то я понял что мантиссу тоже на 16 умножить надо, но вы это не написали.

    • Мантиссу и не нужно умножать на 16, просто её нужно на своё место написать (биты 4-31). Но в принципе всё правильно, если вообще не делить это число на мантиссу и дробную часть, то фактически так и получится — просто умножаем десятичную дробь на 16 и округляем до целого.

      • Если не нужно,то как вы из 0x33 получили из 0x330?

      • В вашей формуле 16*USARTDIV умножение идёт и на мантиссу и на дробную часть.

      • USARTDIV — это беззнаковое число с фиксированной точкой, которое закодировано в регистре USART_BRR.

        • Читайте внимательно. При записи целой части в мантиссу не нужно умножать эту целую часть на 16. Имеется ввиду, что в мантиссе эта целая часть должна быть как есть. Мантисса — это не весь регистр, а только его часть (старшие биты). Для того, чтобы записать эту целую часть числа в ту часть регистра, где расположена мантисса, эту целую часть естественно нужно умножить на 16 (или сдвинуть на 4 бита влево, что то же самое), иначе она просто в мантиссу не попадёт. Но в самой мантиссе она будет ни на что не умноженная, вот что имеется ввиду.

          • Ок. Понял. DIV_Fraction используется как сдвиг если умножить его на 16.
            И USART_BRR читается как одно число: мантисса и DIV_Fraction*16

          • Правильно ли я понимаю что при дискретизации 8, формула должна измениться?
            fck / (8*baudrate)

          • С какой целью вообще в регистр USART_BRR записывается значение в 16 раз меньше baudrate с учётом тактовой частоты?
            То что записывается в USART_BRR при актуальной частоте тактирования и baudrate делит частоту тактирования на участки равные нужной нам baudrate * семплирование.
            В примере ниже: частота тактирования 30 делится на равные участки по 10 бит.
            2 baudrate*5 семплинг
            Например:
            30 Частота тактирования
            2 baudrate
            семплинг 5
            В регистре USART_BRR: 30/2*5=3

            Если при частоте тактирования 30 baud, одновременно запустить другую частоту в 3 baud, то частота 30 baud разделится на три части, каждая из которых будет по 10 бит.
            На 3 baud приходится 10 бит при тактовой частоте 30 baud.
            То есть 30 baud делится на 3 части, в каждой из этих частей нужные нам 2 baud*5 семплинг.
            На каждый бит приходится семплинг равный 5.

            В итоге: В регистре USART_BRR лежит значение, которое может считать каждые 10 бит при заданной тактовой частоте(при данных из этого примера).
            Что считать я не знаю, может семплинг или частоту или то и другое.

          • То есть каждые 5 бит можно отсчитывать один реальный бит для передачи в том случае если частота будет 3 baud.

          • Да, странно. Желаемая частота у меня 2 baudrate, а при семплинге 5 у меня получилась возможная частота 3 baudrate.
            Это нормально?

          • Мне ответили на форуме:

            >>24000000/115200==208
            >>Число 208 Что это? Это не скорость передачи данных.
            USART1->BRR = 208
            Как я уже сказал, BRR = APBxClock / Baud
            Логика не занимается математикой, это счетчик, действующий как делитель. Это относительно просто сделать с логическими вентилями и, следовательно, с транзисторами. Внутри UART нет кода, это машина.
            16-кратная передискретизация хорошо сочетается с 4-битной дробной моделью.
            BRR НЕ является скоростью передачи данных
            Скорость передачи = APBxCLK / USARTx->BRR
            Скорость передачи = 24 000 000/208
            Скорость передачи = 115384,62, т. е. 115200 + 0,16%
            ББР = 2500
            24 000 000 / 2500
            9600 бод

      • Но я думаю немного по другому происходит
        Я думаю в регистре BRR хранится число, которое нужно умножить на нужную дискретизацию: 8 или 16.
        И тактовую частоту разделить на это число.
        Например:
        24000000/16*115200==13
        Число 13 хранится в регистре BRR, это число надо умножить на нужную дискретизацию.
        13*16==208
        И тактовую частоту разделить на получившееся число:
        24000000/208==115 384 Это скорость передачи данных при дискретизации 16.
        Или 13*8==104
        24000000/104==230 769 скорость передачи данных при дискретизации 8.
        Так можно увеличить скорость передачи данных за счёт уменьшения дискретизации.

        • Мне кажется вы слишком сильно с этим заморачиваетесь. Зачение в регистре — это просто делитель, который используется для определения скорости передачи по приведённой формуле.
          Есть ли какая-нибудь настройка, чтобы изменить коэффициент в этой формуле на 8? Нет такой настройки.
          Почему именно 16? Для удобства. Для делителя используются 16 бит регистра, что при частоте PCLK2 = 72 МГц даёт возможность запрограммировать частоту передачи от 68 бод. Если бы не было коэффициента — было бы почти от килобода, что как бы отрезает слишком маленькие стандартные скорости (75, 110, 150, 300, 600 бод), а так вроде как норм, все стандартные скорости охвачены. IMHO

          • >>Почему именно 16?
            Как мне кажется это для того чтоб в регистр BRR можно было записать 2 числа.
            В DIV_Mantissa лежит скорость передачи данных, когда в одном бите этой частоты находятся все 115200 бит * на частоту дискретизации. Я проверял, это так. В одном бите при скорости 13 бод находятся 115200*16 бит.
            А если брать весь регистр: то в этих двух регистрах находится делитель.
            С этими двумя числами можно многое делать. Это трюк такой, хитрый класный трюк.
            24000000/16*115200==13==HEX D Это лежит в DIV_Mantissa
            24000000/115200==208==HEX D0 Это лежит во всём регистре DIV_Mantissa + DIV_Fraction

            Имеем 2 цифры: 208 и 13
            Чтоб найти скорость передачи данных при дискретизации 16, надо
            13*16==208
            24000000/208==115 384
            Чтоб найти скорость передачи данных при дискретизации 8, надо
            13*16==104
            24000000/104==230 769
            Если дискретизация 16, то можно не совершать лишнее действие 13*16==208, а сразу брать число из всего регистра BRR: DIV_Mantissa + DIV_Fraction
            ——————
            То что выше это использование числа 13 для нахождения делителя, второе предназначение числа 13 для нахождения начала и конца пакета из 115200*16 бит. Так как в 13 бод находятся 115200*16 бит.
            При любой дискретизации столько бит.
            230 769*8==1 846 152
            115200*16==1 843 200

          • В формуле ведь явно не 208 на 8 и на 16 умножаются.
            Из примера выше: умножаются на 13
            13*16
            13*8

          • Число 208 это не 208.
            Это D0 в HEX
            Это 13
            D==13
            0==0 Это дробная часть числа 13 равна 0
            13 это число, которое нужно умножить на нужную дискретизацию: 8 или 16

            Так думаю понятней.

          • Почему в формуле используется деление на 16.
            Думаю так очень удобно находить нужный делитель.
            Так как я описал выше.
            Если бы в формуле не было деления на 16, то в регистр BRR записалось бы число 208.
            И как теперь найти делитель для семплирования 8? 208/2? Нелогичное вычисление.
            13*16
            13*8
            Так намного понятней.

  • То есть в итоге можно 0d50.99*16= 815,84 и округлить до целого== 816
    Просто наверное умножитель стоит на регистре USART_BRR для того, чтоб слишком большое число не надо было писать в этот регистр.
    Слишком перемудрили вы с вычислениями.

  • Запутали насколько это можно было.
    Так понятней: fck / (16*baudrate)
    fck == частота тактирования
    baudrate == желаемая скорость передачи/приёма
    24000000 / (16 * 9600) = 156,25. Целая часть 156 — пойдет в DIV_Mantissa без изменений, а дробную часть 0.25 умножаем на 16

    • Ну дак, так и написано в формуле 🙂
      Можно, как вы и сказали, ещё проще — тупо разделить fck на baudrate и округлить. Полученное значение просто пишем в регистр USARTDIV и не зааморачиваемся где там мантисса, где не мантисса. Но в доке было так, как выше в статье, поэтому, собственно, я так и написал (для общего развития так сказать, чтобы знали, что там есть всякие целые части, мантиссы и вот это вот всё).
      Дальше все преобразования и так очевидны. Наверное…

  • А зачем в двух регистрах USART_СR1 и USART_СR2 есть одинаковая настройка?
    PCE

Добавить комментарий