stm32, 2 вопрос по USART + ваш совет

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
Родился
Сообщения: 19
Зарегистрирован: Пт янв 08, 2016 19:00:40

Сообщение godz320 »

Добрый день,

Вкратце расскажу, хотел поюзать Ардуино, поучиться там, поиграться. Заказал кучку недорогих плат , ну и мелочку всякую типа esp8266 и NRFки.
Так вот, первое что пришло , STM32F103C8T6 ARM STM32 Minimum System Development Board Module ForArduin за 180 рублей!! :)
Короче, пока почитал про это чудо, я понял, что это что-то более серьезно чем конструктор ардуино ну и почувствовал, что эта игрушка точно для меня.

Прошу помочь сделать пару шагов в правильную сторону.

Для того, чтобы начать что-то делать, нужно получать (выводить) результаты . Значит, первое что нужно, это USART.

Накидал небольшой код, вроде показался универсальный и как кажется быстрый способ отправлять данные по USART, пока только отправлять.

Вопросики:
1. Насколько правильно написал?

Код: Выделить всё

#include "stm32f10x.h"   
#include "stdio.h"
#include "string.h"
#include "stdbool.h"


// Буфер на отправку, тестовый №1. Показывающий, как функция будет выходить с ошибкой 
//#define SENDBUFFERSIZE	5

// Буфер на отправку, тестовый №2. Показывающий, как функция ждет пока освободится буфер 
//#define SENDBUFFERSIZE	10

// Буфер на отправку
#define SENDBUFFERSIZE	50
// Скорость передачи USART
#define BAUDRATE 9600


// Глобальные переменные 

int cntDataBuff = 0;  // кол-во данных в буфере для отправки
int cntSent = 0; // кол-во отправленных данных
char buffToSend [SENDBUFFERSIZE]; // буфер данных для отправки
char CR [2] = { 0x0D, 0x0A }; // перевод строки

// Обработчик прерываения по USART1
void USART1_IRQHandler()
{
			// если регист данных для отправки свободен
			if(USART1->SR & USART_SR_TXE)
			{
				// если кол-во данных для отправления не равно отправленных данных
				if(cntDataBuff != cntSent) 
				{
						// тогда, отправляем 
						USART1->DR = buffToSend[cntSent];
					// и увеличием счетчик отправленных данных
						cntSent++;
				}
				else
				{
					// иначе запрещаем прерывание
						USART1->CR1 &= ~USART_CR1_TXEIE;
					// очищаем кол-во данных для отправки
						cntDataBuff = 0; 
					// и считчика отправленных данных
						cntSent = 0;		
				}
				
			}
			
}



void initUsart()
{
	//	Включаем питание на порт А
	RCC->APB2ENR |= RCC_APB2ENR_IOPAEN ;		
	//	Включаем питание на USART1
	RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
	//	Включаем питание на альтернативные функции 
	RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
	
	// Пин 9: 2 Мгц, альтернативный , Push-Pull
	GPIOA->CRH |= GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9_1;
	// Пин 10: 2 Мгц, альтернативный , Push-Pull
	GPIOA->CRH |= GPIO_CRH_CNF10_1 | GPIO_CRH_MODE10_1;  
	
	// Устанавливаем скорость обмена
	USART1->BRR =  (SystemCoreClock / BAUDRATE);  //0x1D4C;
	// Включаем USART1 , разрешаем отправку данных
	USART1->CR1 |= USART_CR1_UE | USART_CR1_TE;
	
	// Устанавливаем прерывание по USART1
	NVIC_EnableIRQ(USART1_IRQn);
	
}



// Функция отправки данных , на входе string
// выход true - OK, false - недостаточно буфера  
bool sendToUsart(char* text)
{
	
	// для цикла
	int i;
	// получаем размер входной сроки
	int lenghtText = strlen(text);
	
	// если размер входной строки больше буфера, выходим с ощибкой
	if(lenghtText > SENDBUFFERSIZE) return false;
	
	// ждем, если буфер заполнен. Задержки не будет, если буфер будет правильный подобран.
	while( (lenghtText + cntDataBuff) > SENDBUFFERSIZE );
	
	// запрещаем прерывание на то, что регист данных для отправки свободен
  // нужно это для того, чтобы корректно вычеслить новые поступающие данные
	USART1->CR1 &= ~ USART_CR1_TXEIE;
	
	// заполняем буфер или добавляем в буфер новые данные
	for(i = 0;i < lenghtText; i++)
	buffToSend[i + cntDataBuff] = text[i]; //cntDataBuff, размер данных в буфере
	
	// вычесляем новый размер данных в буфере
	cntDataBuff = cntDataBuff + lenghtText;
	
	// разрешаем прерывание на то, что регист данных для отправки свободен
	USART1->CR1 |= USART_CR1_TXEIE;
	
	// выходим без ошибки
	return true;
	
}
//
int main(void)
{
	// Инициализация USART1
	initUsart();
	
	
	//Вызываем функцию отправки
	sendToUsart("Hello, ");
	// тут же шлем еще
	sendToUsart("my name is");
	// и еще
	sendToUsart(" Aleksei");
	// перевод строки
	sendToUsart(CR);
	
	
	while(1){}
	
	
	
}




2. Имеет ли смысл использовать DMA для USART с небольшыми отправками? Или сразу уже делать нормальную функцию?!

3. Как писать код на c++ в KEIL используя ООП?



Заранее спасибо за ответы
Реклама
Прорезались зубы
Сообщения: 219
Зарегистрирован: Вт июл 02, 2013 09:17:49

Сообщение misyachniy »

Программисты на С любят писать поменьше ;-)

1)
sendToUsart(" Aleksei");
sendToUsart(CR);

Можно заменить sendToUsart(" Aleksei\r\n");

2)
При интенсивном обмене обычно делают кольцевые буферы.
Но для начала можно использовать и обычные

3)
// вычесляем новый размер данных в буфере
cntDataBuff = cntDataBuff + lenghtText;

можно заменить
// вычисляем новый размер данных в буфере
cntDataBuff += lenghtText;
Реклама
Родился
Сообщения: 19
Зарегистрирован: Пт янв 08, 2016 19:00:40

Сообщение godz320 »

misyachniy писал(а):Программисты на С любят писать поменьше ;-)

1)
sendToUsart(" Aleksei");
sendToUsart(CR);

Можно заменить sendToUsart(" Aleksei\r\n");

2)
При интенсивном обмене обычно делают кольцевые буферы.
Но для начала можно использовать и обычные

3)
// вычесляем новый размер данных в буфере
cntDataBuff = cntDataBuff + lenghtText;

можно заменить
// вычисляем новый размер данных в буфере
cntDataBuff += lenghtText;
спасибо,

1. В кейле не получилось сразу поставить управляющие символы, сейчас вроде работают :)
2. Подумаю по поводу кольцевого буфера
3. Реально мой косяк:)



По поводу DMA И USART, что-то у меня не получается сделать правильно.

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

Может конечно это связано с остановкой Dma канала во время передачи, но нечего плохого про это не нашел в даташите.

вот код:

Код: Выделить всё


#include "stm32f10x.h"   
#include "stdio.h"
#include "string.h"
#include "stdbool.h"

void initUsart(void);
void TicOneSec(void);
bool sendToUsart(char* text);


// Буфер на отправку, тестовый №1. Показывающий, как функция будет выходить с ошибкой 
//#define SENDBUFFERSIZE	5

// Буфер на отправку, тестовый №2. Показывающий, как функция ждет пока освободится буфер 
//#define SENDBUFFERSIZE	10

// Буфер на отправку
#define SENDBUFFERSIZE	100
// Скорость передачи USART
#define BAUDRATE 9600


// Глобальные переменные 
char buffToSend [SENDBUFFERSIZE]; // буфер данных для отправки
int cntDataBuff = 0;  // кол-во данных в буфере для отправки

// Обработчик прерываения по DMA 4 канал
void DMA1_Channel4_IRQHandler(void)

{
			//если данные все отправлены
		if( DMA1->ISR & DMA_ISR_TCIF4)
		{
			// сбрасываем бит, transfer complete
			DMA1->IFCR |= DMA_IFCR_CTCIF4;
			
			// выключаем Dma канал 4
			DMA1_Channel4->CCR &= ~ DMA_CCR4_EN;

			// очищаем кол-во данных для отправки, типа все отправили
						cntDataBuff = 0; 

		}


}


void initUsart(void)
{
	//	Включаем питание на порт А
	RCC->APB2ENR |= RCC_APB2ENR_IOPAEN ;		
	//	Включаем питание на USART1
	RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
	//	Включаем питание на альтернативные функции 
	RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
	//	Включаем питание DMA1
	RCC->AHBENR |= RCC_AHBENR_DMA1EN;

	
	// Пин 9: 2 Мгц, альтернативный , Push-Pull
	GPIOA->CRH |= GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9_1;
	// Пин 10: 2 Мгц, альтернативный , Push-Pull
	GPIOA->CRH |= GPIO_CRH_CNF10_1 | GPIO_CRH_MODE10_1;  
	
	// Устанавливаем скорость обмена
	USART1->BRR =  (SystemCoreClock / BAUDRATE);  //0x1D4C;
	// Включаем USART1 , разрешаем отправку данных
	USART1->CR1 |= USART_CR1_UE | USART_CR1_TE ; // |USART_CR1_TXEIE;
	// Разрешаем использовать DMA на передачу 
	USART1->CR3 |= USART_CR3_DMAT ;


	// Настройка DMA
	//------------------------------------

	DMA1_Channel4->CCR = 0; // очищаем конфигурационный регистр
	DMA1_Channel4->CCR |= DMA_CCR4_MINC; //  Memory increment mode
	DMA1_Channel4->CCR |= DMA_CCR4_TCIE; // Transfer complete interrupt enable
	DMA1_Channel4->CCR |= DMA_CCR4_DIR; // режим данные из памяти 
	DMA1_Channel4->CPAR = (uint32_t)&(USART1->DR); //  Адрес дата регистра USART1, куда посылать
	DMA1_Channel4->CMAR = (uint32_t)&buffToSend[0]; // Начайльный адрес в памяти откруда брать
	

	// Устанавливаем прерывание по DMA канал 4
	NVIC_EnableIRQ(DMA1_Channel4_IRQn);
	
}



bool sendToUsartDma(char* text)
{

	// для цикла
	int i;	

	// получаем размер входной сроки
	int lenghtText = strlen(text);

	
	// если размер входной строки больше буфера, выходим с ощибкой
	if(lenghtText > SENDBUFFERSIZE) return false;

	
	// заполняем буфер или добавляем в буфер новые данные
	for(i = 0;i < lenghtText; i++)
	buffToSend[i + cntDataBuff] = text[i]; //cntDataBuff, размер данных в буфере


	DMA1_Channel4->CCR &= ~DMA_CCR4_EN; // останавливаем канал DMA
	
	// новое кол-во байт для передачи, учитывая сколько байт было еще не отправлено
	cntDataBuff = DMA1_Channel4->CNDTR + lenghtText;
	
	DMA1_Channel4->CNDTR = cntDataBuff ; // кол-во байт для передачи

	DMA1_Channel4->CCR |= DMA_CCR4_EN; // включаем канал

	// выходим без ошибки
	return true;

}


int main(void)
{
	
	// Инициализация USART1
	initUsart();
		

	sendToUsartDma("1234567890");
	sendToUsartDma("1234567890");
	sendToUsartDma("1234567890");
	sendToUsartDma("1234567890");


	
	while(1){}
	
	
	
}




Прорезались зубы
Сообщения: 219
Зарегистрирован: Вт июл 02, 2013 09:17:49

Сообщение misyachniy »

godz320 писал(а): По поводу DMA И USART, что-то у меня не получается сделать правильно.
Проверять код не буду, лень :-)
В Интернете полно примеров
например
http://microtechnics.ru/stm32-uchebnyj-kurs-dma/

наиболее часто встречающаяся проблема с DMA - это выравнивание буфера.
В типовом случае он должен быть выровнен на границу 4 байт.
Если все примеры с Интернета не заработатют :-) поищите в руководстве на компилятор
"pragma pack(n)"
Реклама
Эиком - электронные компоненты и радиодетали
Родился
Сообщения: 19
Зарегистрирован: Пт янв 08, 2016 19:00:40

Сообщение godz320 »

misyachniy писал(а):
godz320 писал(а): По поводу DMA И USART, что-то у меня не получается сделать правильно.
В Интернете полно примеров
например
http://microtechnics.ru/stm32-uchebnyj-kurs-dma/
"

у меня немного другая задача. Все примеры в основном ждут завершения передачи DMA, я же пытаюсь сделать динамическую передачу в пределах своего буфера.
То есть принцип работы такой (буфер возьмем 100 байт):

1. первая отсылка (10 байт), записываю в буфер первые 10 байт, запускаю DMA с кол-во передачи 10 байт.
2. Сразу вторая посылка (10 байт), добавляю в буфер следующие 10 байт . Уже 20 байт в буфере, останавливаю DMA канал, спрашиваю сколько уже успел отправить, и дописываю в счетчик еще 10 байт. То есть если DMA за это время успела передать 1 байт, значит 19 байт ставлю в счетчик.. Запускаю DMA на продолжение.
ну итп вобщем.

все работает, но коряво с первой отсылкой.

вот результат работы:

11234567890
123456789
1234567890
1234567890
Реклама
Мучитель микросхем
Сообщения: 469
Зарегистрирован: Вс авг 30, 2015 03:52:59

Сообщение Pnjom-Penb »

misyachniy писал(а):поищите в руководстве на компилятор "pragma pack(n)"
Подумал вот о чем - прагма-паки иногда отличаются в разных компиляторах, и это создает некоторую помеху переносимости, с другой стороны:
  • 1.Размер int'а в языке Си равен размеру слова целевой платформы,
    2.Выравнивание по-умолчанию, емнип, как раз на границу слова и выбирается
а тогда - можно поместить буфер в union с одним int'ом и все остальное любой компилятор сделает сам.
Реклама
Прорезались зубы
Сообщения: 219
Зарегистрирован: Вт июл 02, 2013 09:17:49

Сообщение misyachniy »

Pnjom-Penb писал(а): 1.Размер int'а в языке Си равен размеру слова целевой платформы,
2.Выравнивание по-умолчанию, емнип, как раз на границу слова и выбирается
а тогда - можно поместить буфер в union с одним int'ом и все остальное любой компилятор сделает сам.
1 Размер int для IAR AVR 16 бит, хотя контроллер 8 битный.
2 Выравнивание выбирается с учетом опций компилятора.
Если дать оптимизаию по размеру, то может попаковать непредсказуемым образом.
Родился
Сообщения: 19
Зарегистрирован: Пт янв 08, 2016 19:00:40

Сообщение godz320 »

мужики, давайте вопрос задам по другому,

корректно ли остановить DMA канал во время передачи данных и потом продолжить его работу?
Друг Кота
Сообщения: 4583
Зарегистрирован: Вс дек 05, 2010 06:10:34
Откуда: ЮВ

Сообщение HHIMERA »

misyachniy писал(а): наиболее часто встречающаяся проблема с DMA - это выравнивание буфера.
В типовом случае он должен быть выровнен на границу 4 байт.
Надумано... это не старый АРМ...
godz320 писал(а): 1. первая отсылка (10 байт), записываю в буфер первые 10 байт, запускаю DMA с кол-во передачи 10 байт.
2. Сразу вторая посылка (10 байт), добавляю в буфер следующие 10 байт . Уже 20 байт в буфере, останавливаю DMA канал, спрашиваю сколько уже успел отправить, и дописываю в счетчик еще 10 байт. То есть если DMA за это время успела передать 1 байт, значит 19 байт ставлю в счетчик.. Запускаю DMA на продолжение.
ну итп вобщем.
Чревато... и странно...
Pnjom-Penb писал(а):можно поместить буфер в union с одним int'ом и все остальное любой компилятор сделает сам.
И в чём тайный смысл???
godz320 писал(а): корректно ли остановить DMA канал во время передачи данных и потом продолжить его работу?
Так как вы хотите... нет... А так... не запрещено...
"Я не даю готовых решений, я заставляю думать!"(С)
Родился
Сообщения: 19
Зарегистрирован: Пт янв 08, 2016 19:00:40

Сообщение godz320 »

HHIMERA писал(а):
godz320 писал(а): корректно ли остановить DMA канал во время передачи данных и потом продолжить его работу?
Так как вы хотите... нет... А так... не запрещено...
вот это важно, спасибо. Вроде работает, но чувствую что-то не то.

То есть хотите сказать, в любом случае я должен дождаться окончания транзакции DMA?
Мучитель микросхем
Сообщения: 469
Зарегистрирован: Вс авг 30, 2015 03:52:59

Сообщение Pnjom-Penb »

misyachniy:
1 ... хотя контроллер 8 битный - В самом деле, про аврки-то я и забыл. :)
2 Выравнивание выбирается с учетом опций компилятора - и каково значение по-умолчанию? У AvrIAR'а - именно 1 байт, у ArmIAR'а - 4 байта. (Что-то не найду, какими опциями ком.строки это меняется - хелп молчит, как партизан.)
Если дать оптимизаию по размеру, то может попаковать непредсказуемым образом - всегда полагал, что это не так; сейчас специально залез в описание опций оптимизации ArmIAR'а и не нашел там ни одного указания на это:
EWARM Development Guide писал(а):

Изображение

Где об этом почитать?
HHIMERA писал(а):И в чём тайный смысл???
Ровно в том, что я явно написал.
Вложения
ArmIAR-optimizations.png
(16.16 КБ) 1457 скачиваний
Друг Кота
Сообщения: 4583
Зарегистрирован: Вс дек 05, 2010 06:10:34
Откуда: ЮВ

Сообщение HHIMERA »

Т.е. ... ты ещё и гордишся... своим явным бредом??? Нахрен там юнион... расскажи... посмеёмся...
"Я не даю готовых решений, я заставляю думать!"(С)
Мучитель микросхем
Сообщения: 469
Зарегистрирован: Вс авг 30, 2015 03:52:59

Сообщение Pnjom-Penb »

Сколь бы ни было ошибочно мое предположение, тебя это абсолютно не касается.
Хотя, если ты со смеху лопнешь, то можно считать - вечер удался. Изображение
Друг Кота
Сообщения: 4583
Зарегистрирован: Вс дек 05, 2010 06:10:34
Откуда: ЮВ

Сообщение HHIMERA »

Pnjom-Penb писал(а): вечер удался
Да у тебя... то вечер... то сумрак... то мрак... на постоянной основе...
"Я не даю готовых решений, я заставляю думать!"(С)
Мучитель микросхем
Сообщения: 469
Зарегистрирован: Вс авг 30, 2015 03:52:59

Сообщение Pnjom-Penb »

misyachniy писал(а):2 Выравнивание выбирается с учетом опций компилятора.
Если дать оптимизаию по размеру, то может попаковать непредсказуемым образом.
IAR for ARM v5.0 - опций компилятора, управляющих выравниванием, так пока и не нашел.
Имеем следующую рассказку в доке:

Изображение

Переменные char ложатся и на нечетные адреса, int'ы - на кратные 4:

Код: Выделить всё

char     ch1,ch2;
int      i1;

int main()
{
    printf("&ch1 = %ph\n",&(ch1));    // &ch1 = 102008h
    printf("&ch2 = %ph\n",&(ch2));    // &ch2 = 102009h
    printf("&i1 = %ph\n",&(i1));      //  &i1 = 102000h

    ...
Кладем ch2 в union с int'ом и получаем выравнивание на границу 4:

Код: Выделить всё

char     ch1;  //, ch2;

union    t_chAligned
    {
         int    i;
         char   ch2;
    } chAligned;

int      i1;

int main()
{
    printf("&ch1 = %ph\n",&(ch1));            // &ch1 = 10200Ch
    printf("&ch2 = %ph\n",&(chAligned.ch2));  // &ch2 = 102000h
    printf("&i1 = %ph\n",&(i1));              //  &i1 = 102004h

    ...
Похоже, все работает, как и было описано ("aligned according to its specified type"):

Изображение

Это пока с выключенной оптимизацией. Позже посмотрю, влияет ли оптимизация на упаковку, хотя доки таких указаний не содержат, повидимому.
Вложения
MembrAlignmnt.png
(7.12 КБ) 1506 скачиваний
BasicDataTypesWthAligng.png
(7.1 КБ) 1485 скачиваний
Друг Кота
Сообщения: 4583
Зарегистрирован: Вс дек 05, 2010 06:10:34
Откуда: ЮВ

Сообщение HHIMERA »

Структуры... оптимизация... С утра наберу пива и чипсов...
"Я не даю готовых решений, я заставляю думать!"(С)
Друг Кота
Сообщения: 4583
Зарегистрирован: Вс дек 05, 2010 06:10:34
Откуда: ЮВ

Сообщение HHIMERA »

godz320 писал(а): То есть хотите сказать, в любом случае я должен дождаться окончания транзакции DMA?
Из вашего описания трудно понять что именно вам нужно... Попробуйте, для начала, вообще без ДМА... Когда картинка начнёт вырисовываться... тогда и станет ясно... что лучше... Может там лучше будет вообще ДМА зациклить на полной длине буфера... а сам ДМА пинать таймером... и только следить за половинкой и полным трансфером... может даже в прерывании...
"Я не даю готовых решений, я заставляю думать!"(С)
Родился
Сообщения: 19
Зарегистрирован: Пт янв 08, 2016 19:00:40

Сообщение godz320 »

HHIMERA писал(а):
godz320 писал(а): То есть хотите сказать, в любом случае я должен дождаться окончания транзакции DMA?
Из вашего описания трудно понять что именно вам нужно...
задача, нужно максимально освободить процессор при передачи , используя DMA.


Попробуйте, для начала, вообще без ДМА... Когда картинка начнёт вырисовываться... тогда и станет ясно... что лучше... Может там лучше будет вообще ДМА зациклить на полной длине буфера... а сам ДМА пинать таймером... и только следить за половинкой и полным трансфером... может даже в прерывании...
первый код выложил, там без DMA. Все работает на прерываниях. Поэтому выложил, чтобы покритиковали, так как я новичок.
Прорезались зубы
Сообщения: 219
Зарегистрирован: Вт июл 02, 2013 09:17:49

Сообщение misyachniy »

Pnjom-Penb писал(а): Переменные char ложатся и на нечетные адреса, int'ы - на кратные 4:
Ну это логично. Если переменные "отдельные" то линкер их располагает "плотно" друг к другу.
Если переменные объявлены как массив char и обращение к ним идет побайтно, то линкер может положить массив не выравненый на 4 байта.
А DMA обычно работают с выравнеными данными.

char может быть и 16 битным. Например в серии C2000 Texas Instrument
http://www.ti.com/lsds/ti/microcontroll ... rview.page
Мучитель микросхем
Сообщения: 469
Зарегистрирован: Вс авг 30, 2015 03:52:59

Сообщение Pnjom-Penb »

misyachniy писал(а):Ну это логично. Если переменные "отдельные" то линкер их располагает "плотно" друг к другу.
Я в курсе. Это был базовый вариант для последующего сравнения с ним того выравнивания, которое получается у той же переменной, помещенной в union с int'ом.
misyachniy писал(а):Если переменные объявлены как массив char и обращение к ним идет побайтно, то линкер может положить массив не выравненый на 4 байта.
Да, и это мне известно. И именно для того, чтобы выровнять на границу в 4 байта, в примере, приведенном выше, используется объединение с int'ом.

P.S. У меня дежавю, или я все это уже писал вчера? :)
Ответить

Вернуться в «ARM»