Администрация форума не несёт ответственности за достоверность информации и оставляет за собой право редактировать или в особых случаях даже удалять посты без предупреждения. Спасибо за понимание.

Программирование ATMEL в BASCOM.

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » Программирование ATMEL в BASCOM. » FAQ по Bascom AVR и МК » Соответствие Си и BASCOM


Соответствие Си и BASCOM

Сообщений 1 страница 23 из 23

1

Ввиду того, что в документации, да и просто на других площадках в примерах используют Си, то зачастую у форумчан возникают сложности с их "адаптацией" в BASCOM.
Ваш покорный (...) из этого же числа... ;)

Поэтому решил создать таблицу соответствия (назовем ее так) синтаксиса Си и BASCOM.
Основной упор будет на CodeVisionAVR.

Для сравнения буду приводить фрагменты BASCOM, но особого упора на это делать не буду, т.к. с "местным" синтаксисом все знакомы достаточно хорошо. ;)

Возможно (и даже скорее всего), что не смогу охватить все "на 100%"...
Возможно, где-то будут ошибки и неточности...
Посему прошу не молчать - поправлять, дополнять (лучше через личку). ;)

+1

2

Исходный код должен содержать заголовок с используемым типом микроконтроллера и главную функцию main.
Главная функция -  main - обязательна в любой программе Си – это так называемая точка входа в программу.

До функции main можно подключить необходимые библиотеки, объявить глобальные переменные, константы, настройки.
Библиотека это отдельный файл, обычно с расширением «.h», в котором уже есть заранее написанный код.

Например, используется ATtiny13 и библиотеку для работы с жк дисплеем «alcd.h».

#include <tiny13.h>
#include <alcd.h>

$Regfile="attiny13a.dat"
$crystal = 9600000
$hwstack = 16
$swstack = 16
$framesize = 16

Тело функции выделяется фигурными скобками.
Так же фигурные скобки используются в записи операторов, но об этом позже.

Внутри функции main уже выполняется основная программа. После выполнения функции программа остановится, поэтому делают бесконечный цикл while, который крутит одну и ту же программу постоянно.

void main(void)
{
    while (1)
        {
         ......
        };
};

Главный цикл:
Do
......
Loop

End

В программе BASCOM используется оператор End (выделено цветом), определяющий окончание основной части программы.
Все прочие подпрограммы, блоки DATA и т.п. располагаются после него.
Если фрагмент, оформленный, как подпрограмма (или т.п.) будет расположен ранее Главного цикла, то он будет выполнен, но команда выхода ( Return ) заготовит "подводный камень" или сразу вызовет ошибку.
В "коротких" программах, без дополнительных п/п и блоков, можно обойтись без End.

В Си такого оператора нет.

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

void функция_1()
{
......
};

void main(void)
{
    while (1)
        {
        функция_2();
        };
};

void функция_2()
{
......
};

void функция_3()
{
......
};

Каждый оператор или команда обязательно завершается "точкой с запятой" - ; !
Есть исключения, но об этом позже...

Надо отметить, что при написании имени функции в качестве разделителя должно быть использовано, если есть необходимость, именно "подчеркивание" ( _ ) !
функция_2 - правильно
функция-2 - неправильно
функция*2 - неправильно
функция 2 - неправильно
Это правило распространяется и на имена переменных.

В любой части исходного кода можно написать комментарий, на работу программы он влиять никак не будет, но будет помогать сделать пометки к написанному коду. Закомментировать строку можно двумя слешами //, после этого компилятор будет игнорировать всю строку, либо несколько строк /**/
// одна строка комментария
/*
   несколько строк комменатрия
*/

0

3

Переменные, которые объявляются в заголовке программы, являются "глобальными", т.е. с ними можно работать из любого места программы.
Переменные, объявленные "внутри" функции, являются "локальными", работают только внутри функции (подпрограммы).
Собственно, как и в BASCOM.
Например:
int Var=0;             - объявление глобальной переменной

void функция_2()
{
int Var=0;             - объявление локальной переменной
};

Переменные могут быть следующих типов (справа BASCOM):

bit, _Bit                   0 или 1

Bit

char                        от -128 до 127

-

unsigned char          от 0 до 255

Byte

int                          от -32768 до 32767

Integer

unsigned int            от 0 до 65535

Word

long int                   от -2147483648
                              до 2147483647

Long

unsigned long int     от 0 до 4294967295

Dword

float                       от ±1.175e-38
                              до ±3.402e38

Single

-

Double

Информация о типах переменных BASCOM - Типы переменных в Bascom

Несмотря на наличие в перечне типа Bit, с битовыми операциями в Си напряжёнка... ;)
К примеру, BASCOM'овское написание Var.0 = 1 "не прокатит", но такие команды, как например
PORTB.0=1;
TWCR.TWEN = 1;
...вполне себе работают.
Т.е., с управлением битами на уровне регистров МК все в порядке.

Так же используются массивы.
int Var[3]={0,1,5};
где
- [3] - количество элементов (ячеек) массива
- {0,1,5} - значения, помещаемые в массив
Нумерация ячеек массива всегда ведется с "0" !

Значения в ячейки уже объявленного массива (!) можно записывать индивидуально:
Var[2]=10;

При записи числовых значений, как и в BASCOM, можно использовать различные системы счисления:

Var=2;

Var=2

DEC запись числа

Var=0x02;

Var = &H02

HEX запись числа

Var=0b00000010;

Var = &B00000010

BIN запись числа

В некоторых "сленгах" Си, в частности в CodeVision, нет строковых переменных.
Т.е. нельзя создать переменную:
string hello=”привет”;
Для этого придется создавать массив из отдельных символов.
char hello[6]={'п','р','и','в','е','т'};
Вывод такой "строки", например на LCD, придется выполнять в цикле...

Так же хочется отметить, что синтаксис Си регистрозависим (!), но не во всех "сленгах".
Поэтому может оказаться, что переменные Var и var - разные переменные !

0

4

Работа с портами МК, как уже было упомянуто, ведется через соответствующие регистры.

В Си не получится, как в BASCOM, определить направление порта:
Config PortC = Input
Config PortB.6 = Output
Config PortD = Output

Для этого придется воспользоваться регистрами МК DDRx (в BASCOM аналогично):
DDRC = 0;                                             - порт C включается "на вход"
DDRB.6 = 1;  или DDRB = 0x01000000;  - пин 6 порта B - "на выход", остальные пины порта
                                                              "на вход"
DDRD = 255;                                         - порт D включается "на выход"

Аналогично с регистрами чтения-записи PORTx и PINx.

В некоторых "сленгах", например в редакторе ArduinoIDE, можно встретить такую строку:
pinMode(13, OUTPUT);
Это такое же конфигурирование порта МК, но указывается не конкретный пин порта, а номер на разъеме платы, с которым он соединен.

0

5

Nord написал(а):

Основной упор будет на CodeVisionAVR.

Честно говоря выбор стандарта для сравнения не очень удачен.
CVA  C как-то не очень поддерживает. Так, свой какой-то диалект, похожий на C, реализует.
Если уж начинать эту тему, то лучше взять стандартный язык.
Конечно некоторые операторы и структуры будут похожи, что не должно вводить нас в заблуждение.
Пардон, что вмешался.

+1

6

Арифметические действия в написании практически не отличаются от BASCOM.

Var = 2+2;
Var++

Var = 2 + 2
Var = Var + 1 или Incr Var

Сложение

Var = 2-2;
Var--

Var = 2 - 2
Var = Var - 1 или Decr Var

Вычитание

Var = 2*2;

Var = 2 * 2

Умножение

Var = 2/2;

Var = 2 / 2

Деление

     -                                            Var = 2 \ 2                               Целочисленное деление

А вот в записи логических операций уже присутствует "Си-шная клинопись" (моё) ;)

X==Y

X = Y

X равен Y

X!=Y

X <> Y

X неравен Y

X>Y

X > Y

X больше Y

X<Y

X < Y

X меньше Y

X>=Y

X >= Y

X больше или равен Y

X<=Y

X <= Y

X меньше или равен Y

X !Y

Not

Логическое "НЕ"

X && Y

And

Логическое "И"

X || Y

Or

Логическое "ИЛИ"

X ^ Y

Xor

"Исключающее ИЛИ"
Есть не во всех "сленгах" Си

0

7

Nord написал(а):

Т.е. нельзя создать переменную:
string hello=”привет”;
Для этого придется создавать массив из отдельных символов.
char hello[6]={'п','р','и','в','е','т'};

Это верно лишь отчасти.
Никто не запрещает использовать такую конструкцию: char S[]="Привет мир!";
А в С++ такую:
std::string S1 {"Первая строка"};
std::string S2 = "Вторая строка";
std::string S3("Третья строка");

0

8

sva-don написал(а):

Честно говоря выбор стандарта для сравнения не очень удачен.
CVA  C как-то не очень поддерживает.

Выбор CodeVisionAVR в качества "опорного" вызван тем, что форумчане зачастую обращаются к опыту ардуинщиков, а они в основной массе работают в этой среде.

Информация по CodeVisionAVR довольно скудна и в основном ограничивается "первым уроком" ;) , поэтому, как источником, чаще пользуюсь https://learnc.info/c/

Я не ставлю целью обучению Си, главная задача - привнести синтаксические и стилистические соответствия, чтоб можно было адаптировать найденное к BASCOM. ;)

+1

9

sva-don написал(а):

Это верно лишь отчасти.
Никто не запрещает использовать такую конструкцию:
....

Не спорю. Но для CodeVisionAVR это справедливо.
Опять же повторюсь - нет задачи охватить все "сленги" Си.
Если кому-то при "переводе" с Си в BASCOM встретится указанная вами конструкция, то, надеюсь, это не создаст "трудностей перевода"... ;)

0

10

В ходе работы программы зачастую приходится принимать решение в зависимости от полученного результата.
Для этого служат условия ветвления.
При проверке условия используются логические операторы (см. ранее).

if (условие) {
    <Действия, если условие выполняется>;
}

В написании операторы ветвления сходны с BASCOM.

При проверке нескольких условий используется оператор else:

if (Var == 0) {
        .......
    } else if (Var == 1) {
        .......
    } else if (Var == 2) {
        .......
    } else if (Var == 3) {
        .......
    } else if (Var == 4) {
}

Сразу заметное отличие - отсутствие оператора End If.
В Си его функцию несет закрывающая скобка }

Ветви оператора if могут содержать любые допустимые к использованию внутри функции конструкции.
Например объявление переменных, арифметические действия, вывод на LCD и т.д.
Ветвления могут быть вложены друг в друга.

Так же используется конструкция по типу Select ... Case ... End Select в BASCOM.
Присутствует не во всех "сленгах" Си !

switch (Var) // switch (это оператор) принимает переменную Var и ищет подходящий case
{
case 1:       // если Var равно 1, будут выполнено тело этого case
......
break;        // выход из switch. иначе будет переход case(2) и т.д.
case 2:       // если Var равно 2...
......
break;
case 3:
......
break;
case 4:
......
break;
case 5:
......
break;
default:      // если ни один case не сработал, сработает default
......
}

Операторы ветвления не требуют наличия в конце строки разделителя ; - это одно из исключений, про которые было упомянуто ранее.

0

11

Nord написал(а):

Выбор CodeVisionAVR в качества "опорного" вызван тем, что форумчане зачастую обращаются к опыту ардуинщиков, а они в основной массе работают в этой среде.

Они работают в ArduinoIDE где язык C++ (не путать с Си) и компилятор GCC. К CodeVisionAVR отношения не имеет.

+1

12

Пётр написал(а):

Они работают в ArduinoIDE где язык C++ (не путать с Си) и компилятор GCC. К CodeVisionAVR отношения не имеет.

Абсолютно согласен.
Да, ArduinoIDE как-то вывалился из этой темы...  :blush:
Буду учитывать и включать в описание.

Но в этой среде Си "кастрирован" не меньше, чем в CodeVisionAVR...

Опять же повторюсь - не ставится целью охватить все варианты. ;)

Еще бы найти достойные доки по IAR...  :(
В техдокументации по МК используется.
Вот где гремучая смесь из Си и Ассемблера ! ;)

0

13

Nord написал(а):

Но в этой среде Си "кастрирован" не меньше, чем в CodeVisionAVR...

В ArduinoIDE где язык C++ и компилятор GCC.
GCC используется почти везде. Самый популярный бесплатный компилятор C/C++.

0

14

Описание циклов сходно с аналогичными конструкциями в BASCOM, но с учетом своей "орфографии".

while (Var < 10) {
.......
Var++;
}

While Var < 10
........
Var = Var + 1
Wend

do {
.......
Var++;
} while(Var < 10);

Do
........
Var = Var + 1
Loop Until Var = 10

Так же есть возможность использовать в циклах указание направления и шага счета.

for (Var = 1; Var < 10; Var++) {
........
}

For Var = 1 To 10
........
Next Var

for (Var = 10; Var > -5; Var-0.5) {
........
}

For Var = 10 To -5 Step -0.5
.......
Next Var

0

15

Nord написал(а):

Var < -5

Или Var > -5? -6, -7, -8 - это все меньше -5.

+1

16

В целом на этом пока можно остановиться (не закончить ;) ).

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

Например, в IAR AVR Embedded Workbench в заголовке программы можно встретить такие строки:
#define uchar unsigned char
#define uint  unsigned int
#define ulong unsigned long

По этому диалекту, к сожалению, информации очень мало, а его полное понимание было бы в разы полезней, чем диалектов CodeVisionAVR и ArduinoIDE...
Практически все примеры в техдокументации приведены на нем...
Как уже сказал ранее - эдакая "гремучая смесь" из Си и Ассемблера... ;)

Так же не было задачи изучения классов, структур, указателей и пр. особенностей программирования на Си.

Подобным образом, собственно, дела обстоят и с Basic-ом... ;)
Куча "диалектов" и сред программирования.

0

17

Andrusha написал(а):

Или Var > -5? -6, -7, -8 - это все меньше -5.

Да, разумеется !
Спасибо ! Исправил...

Не уследил при копировании.  :blush:

+1

18

Nord написал(а):

Например, в IAR AVR Embedded Workbench в заголовке программы можно встретить такие строки:
#define uchar unsigned char
#define uint  unsigned int
#define ulong unsigned long

Это просто сокращения. Макросы (автозамена в коде) Чтобы не вводить "много букв".
В коде все записи uchar будут заменены на unsigned char и т. д.

То есть это не какая-то особенность диалекта Си, а стандартная директива препроцессора https://www.c-cpp.ru/books/define

+1

19

Пётр написал(а):

Это просто сокращения. Макросы (автозамена в коде) Чтобы не вводить "много букв".В коде все записи uchar будут заменены на unsigned char и т. д.

Как ALIAS в BASCOM ?

0

20

Инициатива вполне себе полезная, всецело поддерживаю. Переписать код с С на BASCOM или наоборот большого труда не составит.
Просто нужно привыкнуть. При переходе придётся столкнуться со многими трудностями.
Там где раньше был просто PRINT или LCD (и другие простые для программиста вещи) придётся немного потрудится чтобы реализовать всё это.
Но мощь языка конечно неоспорима.

+1

21

Nord написал(а):

Как ALIAS в BASCOM ?

Так точно.  8-)

0

22

sva-don написал(а):

Переписать код с С на BASCOM или наоборот большого труда не составит. Просто нужно привыкнуть. При переходе придётся столкнуться со многими трудностями.

Вот и ткните носом, может что-то упустил / пропустил ? ;)
Думаю, Тема от этого не оскуднеет. ;)

Меня на создание этой Темы сподвигла "война" с т.н. "переводом" в BASCOM примеров на Си из среды IAR "AVR311: Использование модуля TWI в качестве I2C Slave"...  :canthearyou:

0

23

Давайте разберём самый простейший пример передачи по UART.

Код:
#include <avr/io.h>                                       //файл для работы с портами МК
#define F_CPU 16000000UL //16MHz
#include <util/delay.h>

uint8_t i = 0;                                                //Переменная i как байт

void UARTInit(void) {                                    // Настройка UART непосредственной записью в регистры необходимых значений.
    UBRRH = 0;
    UBRRL = 103; //baud rate 9600
    UCSRB = (1<<RXEN)|(1<<TXEN);            // В биты RXEN и TXEN  регистра UCSRB запишем 1
    UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);   //Соответственно запишем 1 в перечисленные биты регистра UCSRC
}

void UARTSend(uint8_t data) {                      //Комментарий ниже
    while(!(UCSRA & (1<<UDRE)));
    UDR = data;
}

int main(void) {                                 //Основной бесконечный цикл 
    UARTInit();
    while(1) {
        i++;
        UARTSend(i);
        _delay_ms(1000);
    }
}

Что представляет собой функция UARTSend(uint8_t data)?
В ней есть строка while(!(UCSRA & (1<<UDRE)));
Это означает, что пока в бите UDRE регистра UCSRA записана 1, будет выполняться пустой цикл. Бит UDRE следит за состоянием регистра данных принятые из UART.
Фактически, программа дожидается того, что буфер на отправку чист (т.е. предыдущий байт передан) и можно передавать следующий байт.
После его очистки помещаем в регистр UDR новый байт который сразу отправляется в COM-порт.
Вот такая песня.
Есть множество других вариантов передачи, но это самый классический.
К счастью в инете есть туча библиотек для работы практически со всей периферией МК. нужно только поискать и не нужно будет заморачиваться с кодом приведённом выше.  :flag:

Отредактировано sva-don (2025-02-07 22:01:48)

+2


Вы здесь » Программирование ATMEL в BASCOM. » FAQ по Bascom AVR и МК » Соответствие Си и BASCOM