Страница 1 из 2
stm32, 2 вопрос по USART + ваш совет
Добавлено: Пт янв 08, 2016 19:25:12
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 используя ООП?
Заранее спасибо за ответы
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Сб янв 09, 2016 12:21:47
misyachniy
Программисты на С любят писать поменьше
1)
sendToUsart(" Aleksei");
sendToUsart(CR);
Можно заменить sendToUsart(" Aleksei\r\n");
2)
При интенсивном обмене обычно делают кольцевые буферы.
Но для начала можно использовать и обычные
3)
// вычесляем новый размер данных в буфере
cntDataBuff = cntDataBuff + lenghtText;
можно заменить
// выч
исляем новый размер данных в буфере
cntDataBuff += lenghtText;
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Сб янв 09, 2016 19:04:31
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){}
}
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Сб янв 09, 2016 19:29:45
misyachniy
godz320 писал(а):
По поводу DMA И USART, что-то у меня не получается сделать правильно.
Проверять код не буду, лень

В Интернете полно примеров
например
http://microtechnics.ru/stm32-uchebnyj-kurs-dma/
наиболее часто встречающаяся проблема с DMA - это выравнивание буфера.
В типовом случае он должен быть выровнен на границу 4 байт.
Если все примеры с Интернета не заработатют

поищите в руководстве на компилятор
"pragma pack(n)"
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Сб янв 09, 2016 19:45:34
godz320
у меня немного другая задача. Все примеры в основном ждут завершения передачи DMA, я же пытаюсь сделать динамическую передачу в пределах своего буфера.
То есть принцип работы такой (буфер возьмем 100 байт):
1. первая отсылка (10 байт), записываю в буфер первые 10 байт, запускаю DMA с кол-во передачи 10 байт.
2. Сразу вторая посылка (10 байт), добавляю в буфер следующие 10 байт . Уже 20 байт в буфере, останавливаю DMA канал, спрашиваю сколько уже успел отправить, и дописываю в счетчик еще 10 байт. То есть если DMA за это время успела передать 1 байт, значит 19 байт ставлю в счетчик.. Запускаю DMA на продолжение.
ну итп вобщем.
все работает, но коряво с первой отсылкой.
вот результат работы:
11234567890
123456789
1234567890
1234567890
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Сб янв 09, 2016 20:16:41
Pnjom-Penb
misyachniy писал(а):поищите в руководстве на компилятор "pragma pack(n)"
Подумал вот о чем - прагма-паки иногда отличаются в разных компиляторах, и это создает некоторую помеху переносимости, с другой стороны:
- 1.Размер int'а в языке Си равен размеру слова целевой платформы,
2.Выравнивание по-умолчанию, емнип, как раз на границу слова и выбирается
а тогда - можно поместить буфер в union с одним int'ом и все остальное любой компилятор сделает сам.
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Сб янв 09, 2016 22:13:57
misyachniy
Pnjom-Penb писал(а):
1.Размер int'а в языке Си равен размеру слова целевой платформы,
2.Выравнивание по-умолчанию, емнип, как раз на границу слова и выбирается
а тогда - можно поместить буфер в union с одним int'ом и все остальное любой компилятор сделает сам.
1 Размер int для IAR AVR 16 бит, хотя контроллер 8 битный.
2 Выравнивание выбирается с учетом опций компилятора.
Если дать оптимизаию по размеру, то может попаковать непредсказуемым образом.
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Сб янв 09, 2016 22:19:46
godz320
мужики, давайте вопрос задам по другому,
корректно ли остановить DMA канал во время передачи данных и потом продолжить его работу?
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Сб янв 09, 2016 22:41:24
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 канал во время передачи данных и потом продолжить его работу?
Так как вы хотите... нет... А так... не запрещено...
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Сб янв 09, 2016 23:19:09
godz320
HHIMERA писал(а):
godz320 писал(а):
корректно ли остановить DMA канал во время передачи данных и потом продолжить его работу?
Так как вы хотите... нет... А так... не запрещено...
вот это важно, спасибо. Вроде работает, но чувствую что-то не то.
То есть хотите сказать, в любом случае я должен дождаться окончания транзакции DMA?
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Вс янв 10, 2016 00:11:22
Pnjom-Penb
misyachniy:
1 ...
хотя контроллер 8 битный - В самом деле, про аврки-то я и забыл.

2
Выравнивание выбирается с учетом опций компилятора - и каково
значение по-умолчанию? У AvrIAR'а - именно 1 байт, у ArmIAR'а - 4 байта. (
Что-то не найду, какими опциями ком.строки это меняется - хелп молчит, как партизан.)
Если дать оптимизаию по размеру, то может попаковать непредсказуемым образом - всегда полагал, что это не так; сейчас специально залез в описание опций оптимизации ArmIAR'а и не нашел там ни одного указания на это:
EWARM Development Guide писал(а):
Где об этом почитать?
HHIMERA писал(а):И в чём тайный смысл???
Ровно в том, что я явно написал.
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Вс янв 10, 2016 00:30:54
HHIMERA
Т.е. ... ты ещё и гордишся... своим явным бредом??? Нахрен там юнион... расскажи... посмеёмся...
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Вс янв 10, 2016 00:39:43
Pnjom-Penb
Сколь бы ни было ошибочно мое предположение, тебя это абсолютно не касается.
Хотя, если ты со смеху лопнешь, то можно считать - вечер удался.

Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Вс янв 10, 2016 00:48:33
HHIMERA
Pnjom-Penb писал(а): вечер удался
Да у тебя... то вечер... то сумрак... то мрак... на постоянной основе...
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Вс янв 10, 2016 02:09:08
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"):

Это пока с выключенной оптимизацией. Позже посмотрю, влияет ли оптимизация на упаковку, хотя доки таких указаний не содержат, повидимому.
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Вс янв 10, 2016 03:04:21
HHIMERA
Структуры... оптимизация... С утра наберу пива и чипсов...
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Вс янв 10, 2016 03:27:56
HHIMERA
godz320 писал(а):
То есть хотите сказать, в любом случае я должен дождаться окончания транзакции DMA?
Из вашего описания трудно понять что именно вам нужно... Попробуйте, для начала, вообще без ДМА... Когда картинка начнёт вырисовываться... тогда и станет ясно... что лучше... Может там лучше будет вообще ДМА зациклить на полной длине буфера... а сам ДМА пинать таймером... и только следить за половинкой и полным трансфером... может даже в прерывании...
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Вс янв 10, 2016 11:01:11
godz320
HHIMERA писал(а):godz320 писал(а):
То есть хотите сказать, в любом случае я должен дождаться окончания транзакции DMA?
Из вашего описания трудно понять что именно вам нужно...
задача, нужно максимально освободить процессор при передачи , используя DMA.
Попробуйте, для начала, вообще без ДМА... Когда картинка начнёт вырисовываться... тогда и станет ясно... что лучше... Может там лучше будет вообще ДМА зациклить на полной длине буфера... а сам ДМА пинать таймером... и только следить за половинкой и полным трансфером... может даже в прерывании...
первый код выложил, там без DMA. Все работает на прерываниях. Поэтому выложил, чтобы покритиковали, так как я новичок.
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Вс янв 10, 2016 11:26:37
misyachniy
Pnjom-Penb писал(а):
Переменные char ложатся и на нечетные адреса, int'ы - на кратные 4:
Ну это логично. Если переменные "отдельные" то линкер их располагает "плотно" друг к другу.
Если переменные объявлены как массив char и обращение к ним идет побайтно, то линкер может положить массив не выравненый на 4 байта.
А DMA обычно работают с выравнеными данными.
char может быть и 16 битным. Например в серии C2000 Texas Instrument
http://www.ti.com/lsds/ti/microcontroll ... rview.page
Re: stm32, 2 вопрос по USART + ваш совет
Добавлено: Вс янв 10, 2016 13:17:20
Pnjom-Penb
misyachniy писал(а):Ну это логично. Если переменные "отдельные" то линкер их располагает "плотно" друг к другу.
Я в курсе. Это был базовый вариант для последующего сравнения с ним того выравнивания, которое получается у той же переменной, помещенной в
union с
int'ом.
misyachniy писал(а):Если переменные объявлены как массив char и обращение к ним идет побайтно, то линкер может положить массив не выравненый на 4 байта.
Да, и это мне известно. И именно для того, чтобы выровнять на границу в 4 байта, в примере, приведенном выше, используется объединение с
int'ом.
P.S. У меня дежавю, или я все это уже писал вчера?
