01.04.2017, 01:34
|
|
Прописка
Регистрация: 29.03.2007
Сообщений: 185
Сказал спасибо: 11
Сказали Спасибо 1 раз в 1 сообщении
|
Глюк приёма данных по USART
Всем доброй ночи! Я недавно создавал тему тут о времени приёма байта по usart. И этот вопрос вытекает как бы из того вопроса. Но я подумал лучше создать отдельную тему, что бы не было путаницы.
Ну, тут я принимаю от gsm модема периодические ответы. А именно звонок. Посылка данных выбрасывается в usart с интервалом в 5 секунд, это как раз те самые гудки, которые вы слышите на той стороне.
Значит посылка данных при звонке выглядит точно вот так:
на картинке 7 посылок, ну то есть было 7 гудков дозвона. Цифры номера замазал, на всякий случай, не к чему он тут)).
вобщем это приём в терминале. И как всегда проблема, когда приёмом занимается МК. Уже полночь и я абсолютно полностью не могу врубиться что у меня не так, хотя мне кажется дело как всегда в какой-нибудь мелкой лаже. Ну так вот. Запускаю отадку в Keil, звоню, смотрю в инспектор переменных. В нём я вижу что каждый раз набивается в буфер при посылке данных.
Абсолютно рандомно эти данные благополучно съезжают. А выглядит это вот так:
То есть часть данных просто берёт и перезаписывается поверх уже существующих в начало. Причём как я уже говорил происходит это рандомно. Бывает при первом гудке, бывает набьётся с 4 гудка,
бывает со 2. Причём я попробовал оставить только одну процедуру набива, закомментив строку Flag_receive_complete = 1; То есть просто, посылка пришла, смотрю что в буфере, далее процедура повторяется.
Ещё пробовал ставить наивысший приоритет прерывания у usart. Так как у меня ещё в проге тикает RTC и пара таймеров. Один из них уходит в прерывание каждые 6 сек. Но я так понял что это особо не влияет.
Ах да... мк stm32f100ret6b, F = 24 MHz
Код:
Код:
|
#include "gsm.h"
#include "lcd.h"
#include ‹string.h›
#define BUFFER_RECEIVE_SIZE 50
char Buffer_receive[BUFFER_RECEIVE_SIZE] = "\0";
uint8_t i = 0, Flag_receive_complete = 0;
void GSM_INI(void){
//USART1
RCC-›APB2ENR |= RCC_APB2ENR_USART1EN; //Вкл. тактирование USART1.
RCC-›APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN; //Вкл. тактирование порта А и альтернативной функции выводов.
GPIOA-›CRH &= ~GPIO_CRH_CNF9_0; //Сброс бита CNF9_0, так как он делает вывод плавающим входом.
GPIOA-›CRH |= GPIO_CRH_CNF9_1; //Режим вывода в альтернативную функцию, выход с подтяжкой.
GPIOA-›CRH |= GPIO_CRH_MODE9_1; //Скорость выхода 2 МГц.
USART1-›BRR = 0x9C4; //9600 бод скорость обмена USART
USART1-›CR1 |= USART_CR1_RXNEIE; //Разрешаем локальные прерывания по приёму USART
USART1-›CR1 |= USART_CR1_IDLEIE; //Включаем прерывания по IDLEIE (завершение приёма строки).
USART1-›CR1 |= USART_CR1_RE | USART_CR1_TE; //Включаем передатчик и приёмник
NVIC_EnableIRQ(USART1_IRQn); //Разрешаем глобальные прерывания для USART1
USART1-›CR1 |= USART_CR1_UE; //Включаем USART
}
void USART1_IRQHandler(void){ //Обработчик прерываний USART1.
if(USART1-›SR & USART_SR_RXNE){ //Если прерывание вызвано по завершению приёма 1 байта.
USART1-›SR &= ~USART_SR_RXNE; //Сбрасываем флаг приёма 1 байта.
Buffer_receive[i++] = USART1-›DR; //Записываем в i-ы элемент массива принятый байт.
}
if(USART1-›SR & USART_SR_IDLE){ //Если прерывание вызвано по завершению приёма строки (реализовано аппаратно по флагу IDLE).
(void)(USART1-›DR & 0xFF); //Читаем геристр DR что бы сбросился бит IDLE.
i = 0;
Flag_receive_complete = 1; //Ставим флаг, что готовы обработать набитый массив.
}
} |
Может это IDLE глючит?... Хотя оно же срабатывает когда линия свободна...А после посылки у меня 5-6 сек тишины... времени достаточно.. хммм...
|
|
|
|
01.04.2017, 02:18
|
|
Модератор
Регистрация: 04.08.2010
Адрес: Москва СЗАО
Сообщений: 11,257
Сказал спасибо: 11,170
Сказали Спасибо 3,858 раз(а) в 2,928 сообщении(ях)
|
Re: Глюк приёма данных по USART
Картинки можно убрать в спойлеры, тогда разметка не пострадает. И лучше таки с рекламой, а не с такими простынями.
Да и их содержимое вполне можно текстом описать. Листать такие простыни не самое приятное из доступных удовольствий, а на мелком тексте ещё и глаза жалко.
Ну и в продолжение к предыдущей Вашей теме - у Вас классический UART и никакого USART. Поменяйте таки названия на соответствующие действительности. Инструкция в разделе о форуме, даже в ЛС писал Вам ссылку.
PS обычная putty умеет писать журнал соединения в обычный файл и нет нужды в первой картинке.
Для второй тоже достаточно просто содержимого буфера с комментариями.
Конечно это требует некоторых телодвижений, но от них есть и польза - в процессе иногда приходит озарение по причине ошибки.
__________________
rtfm forever должно быть основой для каждого. Альтернатива грустна, поскольку метод слепого щенка успешно работает при весьма малом числе вариантов…
Последний раз редактировалось mike-y-k; 01.04.2017 в 02:27.
|
|
|
|
01.04.2017, 05:30
|
|
Вид на жительство
Регистрация: 10.06.2007
Сообщений: 429
Сказал спасибо: 34
Сказали Спасибо 51 раз(а) в 47 сообщении(ях)
|
Re: Глюк приёма данных по USART
Ну если рандомно, то, видимо, что-то рандомно портит переменную i.
Я бы настоятельно рекомендовал быть поосторожнее с глобальными переменными, тем более однобуквенными, тем более с i.
Можно оставить i, но объявить её в обработчике прерывания (в котором она используется) как:
Код:
|
static uint8_t i = 0; |
Хотя если я не ошибаюсь локальные статики и так инициализируются нулём.
А вообще - я бы советовал использовать кольцевой буфер для этого дела.
Ещё нужно посмотреть (если ещё не) - IDLE точно срабатывает когда надо, а не посреди передачи? Эх и не надёжно это... но это не к этой теме - хотите делать так - делайте.
Ну и ещё пару замечаний:
Код:
|
USART1-›SR &= ~USART_SR_RXNE; |
тут & не нужен. В этом регистре флаги сбрасываются записью нуля. запись единицы ничего не меняет. А конкретно этот флаг сбрасывается чтением из USART1-›DR. Так что отдельный сброс этого флага вообще не нужен в этом случае.
Не совсем понял, что это:
Код:
|
(void)(USART1-›DR & 0xFF); |
Чтение из регистра USART1-›DR после чтения USART1-›SR для сброса флага IDLE?
|
|
|
|
01.04.2017, 09:38
|
|
Заблокирован
Регистрация: 07.09.2014
Адрес: В Кремле!
Сообщений: 4,486
Сказал спасибо: 396
Сказали Спасибо 2,220 раз(а) в 1,319 сообщении(ях)
|
Re: Глюк приёма данных по USART
Сообщение от mike-y-k
|
классический UART и никакого USART. Поменяйте таки названия на соответствующие действительности
|
Нинада Модуль зовется USART, и евойный хэндлер тоже зовется USART1_IRQHandler, другое название просто не будет принято (много где переименовывать придется).
А по поводу рандомной перезаписи - у вас индекс буфера - i, глобальная. Вы уверены, что больше нигде эта глобальная i не изменяется случайно?
Последний раз редактировалось NewWriter; 01.04.2017 в 09:40.
|
|
|
|
01.04.2017, 10:15
|
|
Почётный гражданин KAZUS.RU
Регистрация: 12.02.2013
Сообщений: 1,038
Сказал спасибо: 43
Сказали Спасибо 273 раз(а) в 214 сообщении(ях)
|
Re: Глюк приёма данных по USART
Вот где собака порылась
void USART1_IRQHandler(void){ //Обработчик прерываний USART1.
if(USART1-›SR & USART_SR_RXNE){ //Если прерывание вызвано по завершению приёма 1 байта.
USART1-›SR &= ~USART_SR_RXNE; //Сбрасываем флаг приёма 1 байта.
Buffer_receive[i++] = USART1-›DR; //Записываем в i-ы элемент массива принятый байт.
}
if(USART1-›SR & USART_SR_IDLE){ //Если прерывание вызвано по завершению приёма строки (реализовано аппаратно по флагу IDLE).
(void)(USART1-›DR & 0xFF); //Читаем геристр DR что бы сбросился бит IDLE.
i = 0;
Flag_receive_complete = 1; //Ставим флаг, что готовы обработать набитый массив.
}
}
после обработки условия if(USART1-›SR & USART_SR_RXNE) надо выходить из обработчика (return), в вы начинаете проверять условие if(USART1-›SR & USART_SR_IDLE) - и ловите момент когда USART IDLE - это нестабильный код и периодически переменная i обнуляется.
|
|
|
|
01.04.2017, 10:36
|
|
Почётный гражданин KAZUS.RU
Регистрация: 12.02.2013
Сообщений: 1,038
Сказал спасибо: 43
Сказали Спасибо 273 раз(а) в 214 сообщении(ях)
|
Re: Глюк приёма данных по USART
Сообщение от NewWriter
|
Извиняюсь, ваапще-то и без того выходит оттудава - по закрытию скобки }
А в функции, которая имеет тип позвращаемого агрумента void, return как бы и не нужен, при компиляции даже предупреждение выдаст.
|
Да, код будет отрабатываться весь до скобки, завершающей обработчик -
то есть до
}
} ‹-
А команда return без переменной - это просто выход из обработчика.
Если она вас смущает, тогда
goto EXIT; после первого if
}
:EXIT;
}
|
|
|
|
01.04.2017, 21:26
|
|
Прописка
Регистрация: 29.03.2007
Сообщений: 185
Сказал спасибо: 11
Сказали Спасибо 1 раз в 1 сообщении
|
Re: Глюк приёма данных по USART
Сообщение от H4LF
|
Чтение из регистра USART1-›DR после чтения USART1-›SR для сброса флага IDLE?
|
Так точно!
Попробовал сделать вот так:
Код:
|
void USART1_IRQHandler(void){
static uint8_t k;
if(USART1-›SR & USART_SR_RXNE){
Buffer_receive[k] = USART1-›DR;
k++;
return;
}
} |
без всяких idle, просто тупо набив буфера. Даже i сменил на k, k - нигде больше не используется. И опять такая же рандомная фигня. Один в один((( По логике получается что в момент приёма как бы кривой посылки, прерывание usart не срабатывает, а начинает срабатывать только с 31 символа. И что интересно, ни символом до ни после, именно с 31-ого.
|
|
|
|
02.04.2017, 01:25
|
|
Вид на жительство
Регистрация: 10.06.2007
Сообщений: 429
Сказал спасибо: 34
Сказали Спасибо 51 раз(а) в 47 сообщении(ях)
|
Re: Глюк приёма данных по USART
Сообщение от supercelt
|
И что интересно, ни символом до ни после, именно с 31-ого.
|
Так рандомно или что-то именно с 31?
Мне совсем не верится, что это посылка кривая... Скорее софт в камне кривой.
Переменная счётчика в памяти расположена скорее всего после буфера. Как и флаг завершения приёма. А в С отсутствует проверка границ массива(контроль границ на программисте). Не наезжает ли буфер на свой же счётчик, таким вопросом задался я. Без сброса счётчика и за больше одного звонка - точно выкатывается за свой размер. Проверьте это первым делом, это можно сделать и увеличив буфер до 256 элементов, при счётчике uint8_t точно не переполнится (но пойдёт записывать по второму разу).
|
|
|
|
02.04.2017, 02:12
|
|
Прописка
Регистрация: 29.03.2007
Сообщений: 185
Сказал спасибо: 11
Сказали Спасибо 1 раз в 1 сообщении
|
Re: Глюк приёма данных по USART
Рандомно происходит сбой, то есть либо с первого звонка сразу, либо с 3, либо со 2. Но постоянно то, что строка переламывается между символами 9 и ". То есть на 31 символе. Так вот. Увеличил буфер до 254. Сделал так:
Код:
|
void USART1_IRQHandler(void){
//static uint8_t k;
if(USART1-›SR & USART_SR_RXNE){
//USART1-›SR &= ~USART_SR_RXNE;
Buffer_receive[k] = (USART1-›DR & 0xFF);
k++;
return;
}
} |
Записывается норм, до конца массива без накладок.
Но мне то надо узнать что строка передана. Поэтому делаю так:
Код:
|
void USART1_IRQHandler(void){
//static uint8_t k;
if(USART1-›SR & USART_SR_RXNE){
//USART1-›SR &= ~USART_SR_RXNE;
Buffer_receive[k] = (USART1-›DR & 0xFF);
k++;
return;
}
if(USART1-›SR & USART_SR_IDLE){
(void)(USART1-›DR & 0xFF);//Buffer_receive[i++] = USART1-›DR;
k = 0;
//Receive_complete = 1;
return;
}
} |
И вот тут мне попался момент, когда сбой произошёл при 1 звонке. Массив записался до 31 символа, а далее всё остальное записалось поверх в начало. Из чего следует вывод, что IDLE срабатывает почему-то раньше. И рандомно. Возникает вопрос, зачем тогда это idle надо, если оно так криво работает. И всё ещё остался главный вопрос. Как всё-таки гарантировано определить, что вся строка передана, исходя из того, что маркеров конца - нет, и длина строки заранее неизвестна.
Подумал я подумал и решил поставить DMA и на приём. На передаче оно у меня уже есть. Но DMA подразумевает, что размер передаваемых данных известен заранее. Если при передаче strlen рулит, то тут так не прокатит. Я попробовал поставить 50, как и приёмный буфер. Пишется в массив всё замечательно, НО, флаг завершения передачи ставится только после 2 звонка. Потому что при 1 звонке заполняется не весь буфер. А мне бы только 1 раз наполнить буфер от 1 звонка и дальше выключить DMA. Получается DMA тоже как бы не катит.
|
|
|
|
02.04.2017, 02:17
|
|
Прописка
Регистрация: 29.03.2007
Сообщений: 185
Сказал спасибо: 11
Сказали Спасибо 1 раз в 1 сообщении
|
Re: Глюк приёма данных по USART
Ах да, ещё вариант. Прямо в прерывании посимвольно проверять. Но мне кто-то посоветовал сначала набить массив, а потом кромсать его строковыми функциями/
UPD
попробовал убрать idle. Сделал таймер, как было изначально. Настроил на прерывание по переполнению. Взял с запасом 0.5 сек. При прерывании usart каждый раз сбрасываю счётчик этого таймера. По расчётам, после приёма последнего байта проходит 0,5 сек, далее прерывание и ставим флаг конца строки. Но не тут-то было. На 4 звоне опять взял и с 31 символа всё записал поверх в начало(((
Последний раз редактировалось supercelt; 02.04.2017 в 02:53.
|
|
|
|
Ваши права в разделе
|
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения
HTML код Выкл.
|
|
|
Часовой пояс GMT +4, время: 23:20.
|
|