Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 28.08.2021, 18:27   #1
Air
Участник клуба
 
Аватар для Air
 
Регистрация: 30.04.2007
Сообщений: 1,307
Сообщение Если буфер = 0, то почему работает? (Си, AVR)

Доброго времени суток.
Собственно, не знаю есть ли разница обычной программы на Си и прошивки для микроконтроллера, сути ведь не меняет, что читает программу.
Случайно допустил ошибку, но почему всё работает не пойму. У кого какие мысли, вот код:

(Среда AtmelStudio 6)

// Заголовки, подключение библиотек и всё такое
....

Код:
#define SIZE 256 // Глобальная константа, указывает рабочий объём буфера.
uint8_t buff[SIZE]; // Переменная буфера, массив (тип данных byte если бы в C#)
uint32_t addr = 0b00;
uint8_t countByte= 0;

// Далее гора алгоритмов работы с памятью W25Qxx, протоколы SPI, I2C, UART
....

// Вырезка из алгоритма чтения с чипа некоторого объёма информации, от 1 до 256 байт.
memset(buff, 0, SIZE); // "зануляю" массив.

// Собственно сама процедура чтения, принимает этот буфер, указатель на адрес и кол-во требуемых для чтения байт.
W25Q_ReadBlock(buff, &addr, countByte);

В теле этой процедуры, запись в буфер происходит в основном так:
Код:
	for (uint32_t i=0; i < count; i++) // (uint32_t)технически можно весь чип за раз считать, по этому переменная 32бита
	{
		SPDR = 0xFF; // пишем в регистр 256 бит.
		while (!(SPSR&(1<<SPIF))); // ждём готовности чипа.
		buff[i] = SPDR; // читаем с регистра ответ с данными.
	}
Если не знакомы с регистрами протокола SPI сильно не заморачивайтесь, главное, суть записи в переменную массива.

И так, если мы хотим прочитать 128 байт с чипа, то в переменную countByte заносим число 128.
Вызываем процедуру чтения и она спокойно нам возвращает в массив 128 байт информации.
Это нормально.

Процесс записи в чип особо не изменяется.
Код:
	for (uint16_t i=0; i<count; i++) // (uint16_t) за раз записать можно одну страницу (256 байт), но тип 16и битый, что бы не вызвать переполнение счётчика.
	{
		SPDR = buff[i]; // пишем байт в регистр
		while (!(SPSR&(1<<SPIF))); // ждём готовности чипа
		_delay_ms(5); // задержка по даташиту, на запись время тратится.
	}
По прерыванию UART считываем присланный байт и пишем в наш буфер, увеличиваем счётчик и когда необходимо, пишем наш буфер в чип.
Код:
ISR(USART_RXC_vect)
{
	uint8_t byte = UDR; // читаем присланный байт (с компа или ещё откуда)
	buff[countByte] = byte; // Не спрашивайте зачем лишняя переменная, она много где дальше используется, но читать регистр можно только 1 раз.

// Далее процедуры обработки (жутко утомительно)

	countByte++; // увеличиваем индекс, что бы новый байт писать в новую ячейку буфера.

}
Вот собственно и всё.
Приняли байты, записали.
Отправили команду контроллеру, считали байты, отправили дальше.
И это нормально.


А вот что не нормально!
Мне нужно было проверить объём ОЗУ занимаемый не буфером, а всякими переменными, по этому я константу SIZE сделал равной нулю.
Код:
#define SIZE 0
После компиляции, естественно минусовалось 256 байт.
Однако, вернуть на родину константу я забыл и залил прошивку в микроконтроллер.
И к моему удивлению, никаких багов не было!
Читал блоки по 4096 байт (кусками по 256 в потоке).

Вопрос к тем, кто знает или сталкивался с подобным, почему????? Почему программа работала?
Всё гениальное - просто!
Air вне форума Ответить с цитированием
Старый 28.08.2021, 19:03   #2
BDA
МегаМодератор
СуперМодератор
 
Аватар для BDA
 
Регистрация: 09.11.2010
Сообщений: 7,289
По умолчанию

Потому что проверок выхода за границы массива нет. Повезло, что при переполнении не затирали какие-то важные данные, вот программа и работала.
Пишите язык программирования - это форум программистов, а не экстрасенсов. (<= это подпись )
BDA на форуме Ответить с цитированием
Старый 28.08.2021, 20:53   #3
Desc
Участник клуба
 
Аватар для Desc
 
Регистрация: 21.11.2007
Сообщений: 1,063
По умолчанию

Цитата:
Сообщение от Air Посмотреть сообщение
Почему программа работала?
По чему бы и не работать?

Цитата:
Сообщение от Air Посмотреть сообщение
Код:
#define SIZE 0
После компиляции, естественно минусовалось 256 байт.
Не верно.

При объявлении:
Код:
#define SIZE 256 // Глобальная константа, указывает рабочий объём буфера.
uint8_t buff[SIZE]; // size == 2056 bit
получите размер буфера 2 К.байта.
Если объявить:
Код:
#define SIZE 0 // Глобальная константа, указывает рабочий объём буфера.
uint8_t buff[SIZE]; // size == 8 bit
получите размер буфера один байт. Так что размер уменьшился на 2048 бит.

И то что чтение данных выполняется, это нормально. Просто чем меньше буфер тем чаще он будет обновляться.

P. S.
#define SIZE 256 — не константа, макроопределение.
Цитата:
#define - создает макрос, который представляет собой ассоциацию идентификатора или параметризованного идентификатора со строкой токена. После определения макроса компилятор может подставить строку токена для каждого обнаруженного идентификатора в исходном файле.
Link: #define directive (C/C++)
I am not a wizard, I am just learning.

Последний раз редактировалось Desc; 28.08.2021 в 21:49. Причина: Добавил P. S.
Desc вне форума Ответить с цитированием
Старый 28.08.2021, 22:41   #4
Air
Участник клуба
 
Аватар для Air
 
Регистрация: 30.04.2007
Сообщений: 1,307
По умолчанию

Цитата:
Сообщение от Desc Посмотреть сообщение
Цитата:
#define - создает....
С этим да, могу согласиться, просто досконально в терминологии Си ещё не силён.

А вот с этим чёт ни чего не пойму)))
Цитата:
Сообщение от Desc Посмотреть сообщение
При объявлении:
Код:
#define SIZE 256 // Глобальная константа, указывает рабочий объём буфера.
uint8_t buff[SIZE]; // size == 2056 bit
Во 1х
uint8_t это и есть 8 бит, т.е. 1 байт.

Если написать
Код:
uint8_t qwe;
То мы автоматически займём 1 байт памяти, т.е. 8 бит.

Если написать
Код:
uint8_t qwe[1];
То мы выделяем память под одну ячейку массива типоразмером uint8_t т.е. 8 бит, он же 1 байт.

Если написать
Код:
uint8_t qwe[256];
То мы уже выделяем память под 256 ячеек массива типоразмера uint8_t, всё тех же 8 бит и всё того же 1го байта.
Но уже на 256 больше. Согласно арифметике 8 бит * 256 = 2048 бит, это те же самые 256 байт.
Т.е. откуда взялись 2056 не пойму.

Если написать размер ноль... он и в африке будет ноль. Потому что это размер, а не адрес в памяти.

Согласно отчёту компилятора (смотрим скрин)
при SIZE = 0 занятая память 584 байта.
при SIZE = 1 занятая память 585 байт.
при SIZE = 256 занятая память 840 байт.

Теперь снова арифметика.
840 - 256 = 584

Это вполне очевидно доказывает, что
Цитата:
Сообщение от Air
Код:
#define SIZE 0
После компиляции, естественно минусовалось 256 байт.
Во 2х
Цитата:
Сообщение от Desc
получите размер буфера 2 К.байта.
Это физически не реально, потому что в МК ATMega8 всего 1024 байта так называемой ОЗУ.
Если бы получилось 2048 байт, я бы так радовался
Но совершенно логично, если установить SIZE 2048 то в сообщении компилятора получаю
Код:
Data Memory Usage 		:	2632 bytes   257,0 % Full	(Memory Overflow)
P.S.
Цитата:
Так что размер уменьшился на 2048 бит.
2048 бит и 256 байт - это не одно и то же?
Изображения
Тип файла: jpg q1.jpg (78.8 Кб, 1 просмотров)
Тип файла: jpg q2.jpg (77.9 Кб, 1 просмотров)
Тип файла: jpg q3.jpg (79.5 Кб, 2 просмотров)
Всё гениальное - просто!

Последний раз редактировалось Air; 28.08.2021 в 22:49. Причина: Дополнение
Air вне форума Ответить с цитированием
Старый 28.08.2021, 22:57   #5
Desc
Участник клуба
 
Аватар для Desc
 
Регистрация: 21.11.2007
Сообщений: 1,063
По умолчанию

Код:
uint8_t buff1[0] // 8 bits (1 byte)
uint8_t buff2[1] // 16 bits (2 bytes)
array buff1 {0}
array buff2 {0, 1}
Так понятней?

P. S.
Цитата:
Сообщение от Air Посмотреть сообщение
Это физически не реально, потому что в МК ATMega8 всего 1024 байта так называемой ОЗУ.
Вы оптимизацию кода установите равную "0" (none). И посмотрите сколько будет предупреждений, если конечно они не отключены у Вас.
I am not a wizard, I am just learning.

Последний раз редактировалось Desc; 28.08.2021 в 23:08. Причина: Добавил P. S.
Desc вне форума Ответить с цитированием
Старый 28.08.2021, 23:13   #6
Air
Участник клуба
 
Аватар для Air
 
Регистрация: 30.04.2007
Сообщений: 1,307
По умолчанию

Цитата:
Сообщение от Desc Посмотреть сообщение
Код:
uint8_t buff1[0] // 8 bits (1 byte)
uint8_t buff2[1] // 16 bits (2 bytes)
array buff1 {0}
array buff2 {0, 1}
Так понятней?
Если бы... Но компилятор говорит совсем другое.
Если объявить uint8_t buff1[0];
То в коде ниже мы не можем обратиться к нулевому элементу массива.
buff1[0] = 128;
По причине того, что для него не выделена память.
Например в высокоуровневых языках (С#, Delphi) такое действие приведёт к ошибке обращения к памяти. Типа Access violation ....

В МК можно таким образом запортачить рабочие данные, если писать куда попало.
Другими словами ваша запись array buff1 {0} неверна.
Как и array buff2 {0, 1} неверна, потому что вы не выделили память под второй элемент массива.

Позвольте узнать, это в каком языке такое вообще допустимо? (Кроме серверных скриптов PHP)



P.S.
Отключил.
Предупреждения от системного модуля delay.... И то к делу не имеет отношения
Изображения
Тип файла: jpg w1.jpg (84.1 Кб, 1 просмотров)
Тип файла: jpg w2.jpg (83.6 Кб, 1 просмотров)
Тип файла: jpg w3.jpg (105.5 Кб, 3 просмотров)
Всё гениальное - просто!

Последний раз редактировалось Air; 28.08.2021 в 23:17.
Air вне форума Ответить с цитированием
Старый 29.08.2021, 01:53   #7
Desc
Участник клуба
 
Аватар для Desc
 
Регистрация: 21.11.2007
Сообщений: 1,063
По умолчанию

Цитата:
Сообщение от Air Посмотреть сообщение
В МК можно таким образом запортачить рабочие данные, если писать куда попало.
Подозреваю путаете индексы с адресами регистров.
О регистрах речь не веду.

Цитата:
Сообщение от Air Посмотреть сообщение
Другими словами ваша запись array buff1 {0} неверна.
Как и array buff2 {0, 1} неверна, потому что вы не выделили память под второй элемент массива.

Позвольте узнать, это в каком языке такое вообще допустимо? (Кроме серверных скриптов PHP)
Это не запись кода, попытка объяснить резервирование памяти. Видимо не удачная.

Попробую иначе.
Посмотрите что выведет код ниже в обычном консольном приложении.
Код:
#define SIZE 0

    int buff[SIZE];
    int first_num = buff[0];
    std::cout << first_num << std::endl;
    std::cout << buff[0] << std::endl;
    buff[1] = 32767;
    std::cout << buff[1] << std::endl;
    buff[2] = 65535;
    std::cout << buff[2] << std::endl;
    std::cout << sizeof(buff);
Будет предупреждение, и тем не менее оптимизатор сделает свое дело.
И получим
Output:
0
0 // или будет мусор из ОЗУ
32767
65535
0

P. S.
Код:
#define SIZE 1
   uint8_t buff[1]
Зарезервировано 8 байт.
Код:
#define SIZE 0
   uint8_t buff[0]
Как не парадоксально, зарезервировано 8 байт. Т. к. есть объявление uint8_t buff, без инициализации там какой-то мусор.

P. P. S.
Что касается чтения из SpiFlash. Урезав буфер Вы ведь не перекрыли доступ к страницам.
Плюс оптимизатор по объявлению uint8_t buff за Вас определил пространство. Вот и чтение происходит без проблем.
Отключать предупреждения плохая идея. Не заметите просчеты. И конечный продукт даст сбой в самый не подходящий момент.
I am not a wizard, I am just learning.

Последний раз редактировалось Desc; 29.08.2021 в 02:32. Причина: Добавил P. S. + P. P. S.
Desc вне форума Ответить с цитированием
Старый 29.08.2021, 08:08   #8
Алексей1153
фрилансер
Форумчанин
 
Регистрация: 11.10.2019
Сообщений: 960
По умолчанию

Цитата:
Сообщение от Air Посмотреть сообщение
uint8_t buff[256];
uint8_t buff[256]; - зарезервируется 256 байтов
uint8_t buff[1]; - зарезервируется 1 байт
uint8_t buff[0]; - ничего не зарезервируется. "Работать" с таким "массивом" нельзя, это неопределённое поведение
Алексей1153 вне форума Ответить с цитированием
Старый 29.08.2021, 12:24   #9
Air
Участник клуба
 
Аватар для Air
 
Регистрация: 30.04.2007
Сообщений: 1,307
По умолчанию

Алексей1153 Это я понимаю)

Цитата:
Сообщение от Desc Посмотреть сообщение
buff[1] = 32767;
Так как места под массив не зарезервировано и не дай бог, в этот момент произойдёт прерывание, приоритетом выше текущего, в это место может записаться другая информация.
Код:
std::cout << buff[1] << std::endl;
И этот оператор вернёт уже не 32767

Я вот что подумал. Мы говорим о разных видах памяти.
Есть Flash, статическая, программируется во время прошивки МК.
Есть RAM, привычнее ОЗУ. Память которая используется для вычислительных процессов, которая постоянно перезаписывается программой МК.

Те. 8 бит, от типа данных uint8_t, о которых вы говорите, как раз таки пишутся во Flash (на первых скринах это видно)
Логично, если объявить переменную uint32_t то займёт 32 бита. Но во Flash памяти, а не RAM.

Суть моих манипуляций с макросом SIZE в том, что бы увидеть расход именно RAM (озу) памяти. Потому что её очень мало и нужно за частую прибегать к оптимизации, более рационально использовать массивы.
А то, что сама переменная занимает место в статической памяти... Да и бог с ней =)
Занимает она 8 бит или 32, не объясняет, почему программа работала стабильно.
Суть данного поста не в том, что бы посчитать кто сколько чего занимает, а причины, почему данные передавались, почему на дисплее не было "мусора".
Всё гениальное - просто!
Air вне форума Ответить с цитированием
Старый 29.08.2021, 12:31   #10
Air
Участник клуба
 
Аватар для Air
 
Регистрация: 30.04.2007
Сообщений: 1,307
По умолчанию

BDA
Хм, возможно потому что UART прерывание единственное во всём цикле работы.
Отрисовку на дисплей я делал в основном потоке. Который даже не выполнятся, пока не разрешишь.
Всё гениальное - просто!
Air вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Здравствуйте ! в чем проблема,почему почему время исполнения операций не работает ? ion leahu Помощь студентам 6 23.11.2014 19:36
AVR. Почему одно прерывание блокирует другое? Admin2 Assembler - Ассемблер (FASM, MASM, WASM, NASM, GoASM, Gas, RosAsm, HLA) и не рекомендуем TASM 1 27.07.2013 22:30
Формула ЕСЛИ не работает. Почему? cutie_girl Microsoft Office Excel 3 25.10.2012 23:18
Скрипт не работает, если линкую локально, а работает если линкую на .. keen_ JavaScript, Ajax 3 08.03.2012 07:58
AVR ATmega 128: почему не происходит переход? Blondy Assembler - Ассемблер (FASM, MASM, WASM, NASM, GoASM, Gas, RosAsm, HLA) и не рекомендуем TASM 4 06.05.2011 01:28