Вопросы по С/С++ (СИ)

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить
Грызет канифоль
Аватара пользователя
Сообщения: 275
Зарегистрирован: Вс окт 20, 2019 13:03:56

Сообщение Shuspano »

[uquote="NStorm",url="/forum/viewtopic.php?p=3839049#p3839049"]если понимать это.[/uquote]
Это если понимать. Я к сожалению, этого не понимаю, и для меня это сообщение выглядит так: в огороде бузина, а в Киеве дядька.
Аlex писал(а):Мало того, ещё и ledflash закоменчен
Таки да, но я тогда еще до этой ошибки еще не дошел :)

еще такой вопрос. Вот эти подпрограммы, или функции в Си... как их там. Нельзя ли сделать чтоб было как в ассемблере - где хочу, там и вхожу
Например (ассемблер MCS-51) можно сделать так:

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

label0: mov, A, R0
label1: setb C
label2: cpl A
           inc DPTR
           ret
Тут я могу вызвать подпрограмму с любой метки, без разницы с какой. А можно ли сделать тоже самое в сишных void-ах?
Реклама
Поставщик валерьянки для Кота
Сообщения: 1978
Зарегистрирован: Ср июл 17, 2013 13:55:57

Сообщение NStorm »

Не в void'ах, а в функциях. Void лишь возвращаемое значение функции. Сделать-то можно, банально хотя бы так:

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

void foo(int bar) {
  if (bar == 0)
    do0();
  if (bar <= 1)
    do1();
  do2();
}
Только лучше прекращать мыслить ассемблером и смотреть как делают на С. При должном подходе "входить в функцию в любом месте" в С не нужно.
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2723
Зарегистрирован: Пт сен 07, 2018 20:20:02
Откуда: деревня в Тульской губернии

Сообщение ПростоНуб »

Shuspano, так как С выделяет память для локальных переменных из стека при входе в функцию, у функции может быть только одна точка входа. При этом, вполне реально добиться от оптимизатора именно такого кода, создав в одном файле три функции label0(), label1() и label2() и объявив две последние inline.
Грызет канифоль
Аватара пользователя
Сообщения: 275
Зарегистрирован: Вс окт 20, 2019 13:03:56

Сообщение Shuspano »

NStorm писал(а): Сделать-то можно, банально хотя бы так:
ПростоНуб писал(а):При этом, вполне реально добиться от оптимизатора именно такого кода, создав в одном файле три функции label0(), label1() и label2() и объявив две последние inline.
Спасибо большое!
От ассемблера избавится довольно непросто. Заметил, что прежде чем что-то добавить в программу, я сначала представляю как это сделать на ассемблере (и уже почти написал в голове) и только потом иду гуглить как это в Си делать.
Реклама
Эиком - электронные компоненты и радиодетали
Поставщик валерьянки для Кота
Сообщения: 1978
Зарегистрирован: Ср июл 17, 2013 13:55:57

Сообщение NStorm »

я сначала представляю как это сделать на ассемблере
А вот это порочная практика ) Именно она обычно и не дает использовать С правильно и в полной мере. Я серьезно в данном случае. Возможности языка куда больше, чтобы применять прямолинейный подход асма.

Я хоть и написал выше пример, на деле такое в C обычно редко нужно. Обычно логика работы по-другому делается просто.
Реклама
Atz
Встал на лапы
Сообщения: 87
Зарегистрирован: Ср сен 16, 2009 22:44:40

Сообщение Atz »

Нельзя ли сделать чтоб было как в ассемблере - где хочу, там и вхожу
Считается плохой практикой, но конечно, можно. Гуглите "оператор goto в языке Си".

я сначала представляю как это сделать на ассемблере
Делайте всё, что вы делали обычно на ассемблере, только внутри одной функции на Си, никогда не выходя за её пределы — и будем вам "щастье". Это называется "инкапсуляция". А вот практика создания сложных циклов на метках как раз и считается порочной, потому что потом не разобраться в коде.

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

Историческая заметка:
СпойлерВ паскале были процедуры и функции — те и другие являлись подпрограммами, у кого-то были возращаемые значения и параметры, а у кого-то нет. Понятно, что весь этот набор сводится к одним только функциям, поэтому в Си только они и есть.
После этого у вас 100% возникнет вопрос «как сохранять переменные при смене функций». В языке Си вам придётся делать глобальные переменные или структуры и передавать указатели на них в параметры функций. В С++ можно создавать класс: всем функциям этого класса видны все переменные своего класса.

Кусочек вашего примера на упрощённом псевдокоде, похожем на С++:
Спойлер

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

struct MyObjects // во множественном числе, потому что это ещё не сам объект, а их тип.
{
    MyObjects() // конструктор, вызывается автоматически при создании объекта класса
    {
         setup();
    }
protected:
    void setup();
private:
    // описание всех переменных, например "int DDRB;"
public:
    void my_function(); // доступные для вызова снаружи функции класса (его интерфейс).
}
; // здесь кое-что новое для вас: точка с запятой после фигурной скобки оканчивает класс.

void MyObjects::setup()
{
      DDRB  |=  (1 << sdata)|(1 << sclock)|(1 << latch);
      DDRB  &= ~(1 << rdata);
}

void MyObjects::my_function()
{
   // do something wrong, fix it and do something again :)
}

int main(/* параметры главной функции не используем, просто опишем их тип */int, char*[] )
{
    MyObjects object_one;
    MyObjects object_two;
    // всё: setup вызван у обоих объектов автоматически и оба объекта — независимые.
    // можно создавать свои функции и вызывать их так: object_one.my_function();
    // глобальная область видимости при этом остаётся чистая, а каждый объект имеет
    // по своему набору переменных в памяти.
    return 0;
}
Добавлено after 13 minutes 40 seconds:
добавлено: если есть сложности с чтением кода или проблемы зрением, то возьмите Far Manager версии 1.xx (не 2 и не 3), найдите к нему плагин HighLighter и покрасьте себе код по-цветастее, редактируя настройки этого плагина в его папочке и каждый раз перезапуская Far Manager. В редакторе всё станет красивое, а компилировать можно из командной строки.
Реклама
Грызет канифоль
Аватара пользователя
Сообщения: 275
Зарегистрирован: Вс окт 20, 2019 13:03:56

Сообщение Shuspano »

Добрый день.
Решил немного разнообразить свое поделие, и приделать к нему индикатор 1604. Подключить, естественно, к выходам регистра 74hc595. Использовать в 4-битном режиме. И чтоб отправить символ на индикатор, необходимо произвести обмен старшей тетрады (полубайта) между двух переменных типа unsigned char, при этом не испортив младшую тетраду. В ассемблере MCS-51 это делается как два пальца об асфальт: существует инструкция XCHD. В Си похоже ничего такого нету. Пробовал логическими операциями - не получается, не возможно никак заставить, чтоб это делалось только с 4-мя битами.
Что тут можно предпринять?
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

Shuspano писал(а):необходимо произвести обмен старшей тетрады (полубайта) между двух переменных типа unsigned char, при этом не испортив младшую тетраду
Десять раз перечитал и не понял, что нужно сделать
Контактная информация:
Грызет канифоль
Аватара пользователя
Сообщения: 275
Зарегистрирован: Вс окт 20, 2019 13:03:56

Сообщение Shuspano »

WiseLord писал(а):Десять раз перечитал и не понял, что нужно сделать
есть два байта А и Б. Байт состоит из двух тетрад. Мне нужно старшую тетраду байта А переставить в старшую тетраду байта Б, при этом не испортив его младшую тетраду.
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18787
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

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

unsigned char A, B;

// старшую тетраду байта А переставить в старшую тетраду байта Б
B = (B & 0x0F) | (A & 0xF0);
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Грызет канифоль
Аватара пользователя
Сообщения: 275
Зарегистрирован: Вс окт 20, 2019 13:03:56

Сообщение Shuspano »

Спасибо! Можно продолжать.
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

Shuspano, Вообще, как я понимаю, Если Вы пытаетесь завести KS0066 дисплей через 74РС595, то у Вас конфигурация следующая:
D7..D4 дисплея подключены к выходам 7..4 74РС595
BL (подсветка), E (строб), RW (запись/чтение), RS (команда/данные) - подключены к выходам 3..0 соответственно.

Вот вам идея:
- есть лишь специальная глобальная переменная (portData), которая (и только она) отправляется в 595-ку.
- есть лишь одна функция отправки этих данных (sendPortData)

Любые операции записи в дисплей приводятся к этой одной функции, и не более того. И весь базовый код будет очень простым:
Спойлер

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

#define BL_LINE  (1<<3) // подсветка на 3 линии порта
#define E_LINE   (1<<2) // строб на 2 линии порта
#define RW_LINE  (1<<1) // RW на 1 линии порта
#define RS_LINE  (1<<0) // RS на 0 линии порта

static uint8_t portData = 0x00;

static void sendPortData(uint8_t data) {
  // тут пишем функцию отправки байта data
}

static void writeStrob()
{
    sendPortData(portData | E_LINE);    // К имеющимся данным добавляем E
    sendPortData(portData);             // И убираем снова
}

static void ks0066WritePort(uint8_t data)
{
    _delay_us(100);
    portData &= ~RW_LINE;          // RW опускаем

    portdata &= 0xF0;              // Чистим младшую тетраду
    portData |= (data & 0x0F);     // Пишем младшую тетраду из младшей тетрады байта
    writeStrob();                  // Реально пишем данные в 595

    portdata &= 0xF0;              // Чистим младшую тетраду
    portData |= (data >> 4);       // Пишем старшую тетраду из старшей тетрады байта
    writeStrob();                  // Реально пишем данные в 595
}

static void ks0066WriteCommand(uint8_t command)
{
    portData &= ~RS_LINE;       // RS опускаем
    ks0066WritePort(command);
}

void ks0066WriteData(uint8_t data)
{
    portData |= RS_LINE;        // RS поднимаем
    ks0066WritePort(data);
}
Я подобным образом работал с дисплеем, подключенным через I²C расширитель PCF8574 - достаточно популярный вариант подключения.

P.S. чуть подправил код и комментарии
Контактная информация:
Грызет канифоль
Аватара пользователя
Сообщения: 275
Зарегистрирован: Вс окт 20, 2019 13:03:56

Сообщение Shuspano »

Похоже я прогневал кого-то из программистских богов.
Пытался выделить функцию hc595send() в отдельный файл, чтоб спокойно потестить логические функции - она отказалась работать. Притащил осциллограф, чтоб посмотреть чего выдает аттини13, а после измерений он отказался прошиваться через усбасп. Сходил за большим программатором (top-3000), вставил тиньку, сбросил на заводские фузы, все шьется, все хорошо. Подключил к усбасп - не шьется. Опять в большой программатор, подключаю его к компу - венда выдает синего.
Кажется, на сегодня хватит.
Грызет канифоль
Аватара пользователя
Сообщения: 275
Зарегистрирован: Вс окт 20, 2019 13:03:56

Сообщение Shuspano »

Добрый день!
Возникла проблема. Компилятор опять ругается:

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

D:\avr>make func_test.c attiny13a
func_test.c: In function 'main':
func_test.c:123:2: warning: '__progmem__' attribute ignored [-Wattributes]
  const unsigned char lcd_data[] PROGMEM = "Hello, Radiokot";
  ^
func_test.c:125:7: warning: assignment discards 'const' qualifier from pointer t
arget type [enabled by default]
  dptr = lcd_data;
       ^
Явно что-то не так, но при этом (что необычно) все работает так, как хотел. :shock: Догадываюсь, накосячил с указателем, но как?

Функция main():

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

	{
	setup();
	lcd_init();
	lcd_setup();	
	const unsigned char lcd_data[] PROGMEM = "Hello, Radiokot";
	char *dptr;
	dptr = lcd_data;
	int i;
		for (i = 0; i < 14; i ++)
		{
		send_data(*dptr);
		dptr++;
		}
	}
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18787
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Shuspano писал(а):Явно что-то не так, но при этом (что необычно) все работает так, как хотел.
работает - потому что не ошибка, а варнинг.
а не так то, что dptr у вас "указатель на символ" (т.е. на ОЗУ), а массив lcd_data - это "массив неизменяемых символов во FLASH", то есть типы указателей не одинаковы, вот и предупреждает.

совет вам: избавляйтесь от PROGMEM, заменяйте это на корректное и правильное const __flash (вы ж не WinAVR используете? новые версии так могут):

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

const __flash unsigned char lcd_data[] = "Hello, Radiokot";

const __flash char *dptr;
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Грызет канифоль
Аватара пользователя
Сообщения: 275
Зарегистрирован: Вс окт 20, 2019 13:03:56

Сообщение Shuspano »

Спасибо!
Не, у меня блокнот, avr-gcc, и дудка. Я просто знаю как это делать на ассемблере: строка размещается директивой DB, адрес строки в16-битный регистр-указатель, а там - MOVC или LPM. Я погуглил, нашел кое что у DI HALT, и решил что PROGMEM - это аналог этой директивы.
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18787
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Shuspano писал(а):Не, у меня блокнот, avr-gcc, и дудка.
и чо? WinAVR - это довольно древняя версия avr-gcc. если у вас свежая версия - пользуйтесь тем, что я рекомендовал.
Shuspano писал(а):Я просто знаю как это делать на ассемблере
забудьте! если взялись за Си, пользуйтесь средствами Си, чтобы быстрее войти в струю. разумеется, запаситесь справочником каким-нибудь по основным определениям, правилам, функциям и т.п. для Си. и реализовывайте свои алгоритмы уже этими средствами.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 3832
Зарегистрирован: Сб сен 10, 2011 17:46:25

Сообщение oleg110592 »

[uquote="Shuspano",url="/forum/viewtopic.php?p=3841042#p3841042"]Догадываюсь, накосячил с указателем, но как?[/uquote]
можно попрбовать

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

const char *dptr;
Грызет канифоль
Аватара пользователя
Сообщения: 275
Зарегистрирован: Вс окт 20, 2019 13:03:56

Сообщение Shuspano »

Не, так не компиляется

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

D:\avr>make func_test.c attiny13a
func_test.c: In function 'main':
func_test.c:123:2: error: '__flash' specified for auto variable 'lcd_data'
  const __flash unsigned char lcd_data[] = "Hello, Radiokot";
  ^
avr-objcopy: 'func_test.elf': No such file
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18787
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Shuspano писал(а):Не, так не компиляется
вынесите этот массив в область глобальных переменных, то есть за пределы функции, вот и вся проблема.

Добавлено after 1 minute 15 seconds:
кстати, для символьных строк правильнее применять просто char, а не unsigned char - все стандартные функции рассчитаны именно на char - будете ловить варнинги...
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Ответить

Вернуться в «Разные вопросы по МК»