страница в разработке
Cистемы счисления, использованные для программирования микроконтроллеров:
Для работы с портами:
DDRD - регистр направления данных порта D, лог 1 - выход. лог 0 - вход.
PORTB - регистр управление состоянием порта B:
DDRD = 0b11111110; //все пины порта D настроены на выход, а нулевого на вход
PORTD = 0b00000100;//2-й разряд установили в единицу, а все остальные сбрасили в ноль
Установка бита(ов)
PORTB = 0b11111111; //на всех ножках порта B логическая 1, в двоичной системе счисления
PORTB = 0xFF; //на всех ножках порта B логическая 1, в 16-тиричной системе счисления
PORTB = 255; //на всех ножках порта B логическая 1, 10-тиричной системе счисления
PORTD = 0b00000100;//установить 2-ой бит, остальные сбросить.
PORTD = (1<<2); //установить 2-ой бит, остальные сбросить. Чтобы не трогать другие биты:
PORTD = PORTD | (1<<2); //установить 2-ой бит порта D
PORTD |= (1<<2);//короткая запись, установить 2-ой бит порта D
PORTD |= (1<<2)|(1<<6)|(1<<7); //установить второй, шестой и седьмой биты порта D
DDRD |= (1<<4)//уставнить 4-ый бит регистра направления, т.е. перевести 4-ый пин порта D на выход
Разберём подробней запись PORTD |= (1<<2);, зная что существует шесть логических побитовых операций (см. алгебру Буля) и команды выполняются слева на права и сначала в скобках:
Сброс (обнуление) бита(ов)
PORTC = 0b00000000;//сбросить все биты порта (регистра) С
PORTC = PORTC & ~(1<<4);//сбросить 4-ый бит порта С
PORTC &= ~(1<<4);//короткая запись, сбросить 4-ый бит порта С
PORTC &= ~((1<<2)|(1<<4));//сбросить 2-ой и 4-ый бит порта С
DDRС &= ~(1<<4)//перевести порт С пин 4 на вход
Разберём команду PORTC &= ~(1<<4); слева на право:
Переключение (инверсия) бита(ов)
Переключение бита на противоположное состояние: единицу в ноль и наоборот. Рассмотрим на примере порта A:
PORTA ^= (1<<2);//изменим состояние второго бита на пробивоположное
PORTA ^= (1<<0)|(1<<2)|(1<<6);//0-ой, 2-ой и 6-ой биты изменить на противоположное
Проверка разряда на наличие логического нуля
if (0==(PIND & (1<<3))){/*код какой-то*/}//eсли третий разряд порта D сброшен, то...
//аналогичное условие:
if (PIND & (1<<3)){/*код какой-то*/}//если третий пин регистра D равен 0, то... (Пин - ножка микроконтроллера, соответствующая биту порта)
//Ожидание сброса бита, оператор while:
while (PIND & (1<<3)){/*код какой-то*/}//выполнять условие пока 3-ий пин = 1
Проверка разряда на наличие логической единицы
if (0 != (PIND & (1<<3))){/*код какой-то*/}//если 3-ий пин не равен нулю
//Аналогично:
if (~PIND & (1<<3)){/*код какой-то*/}//если 3-ий пин...
//Ожидание установки бита, оператор while:
while (~PIND & (1<<3)){/*код какой-то*/}//Цикл выполняеться пока 3-й разряд порта D сброшен
//или
while (! (PIND & (1<<3))){/*код какой-то*/}//аналогично
Случайные числа
volatile int Temp;
Temp = rand(); //случайное число от 0 до 32768, но при каждом включении они будут одинаковы. Чтобы этого не было нужно менять стартовый аргумент:
srand(5); // стартовое число. Его тоже надо менять, чтоб при следующих запусках программы не было повторений
Temp = rand();
Temp = rand()%10; // генерация от 0 до 9 (%- остаток от деления)
Temp = rand()%5; // генерация от 0 до 4
Temp = 10 rand()%41; // генерация в диапазоне от 10 до 40
Temp = -10 rand()%10; // генерация в диапазоне от -10 до 0
Temp = -20 rand()%10; // генерация в диапазоне от -20 до -10
Temp = -20 rand()%41; // генерация в диапазоне от -20 до 20
volatile float Temp;
Temp = 1.0 (float) rand() / 32768 2.4; // генерация в диапазоне от 1.0 до 2.4
Типы данных С
| Тип | байт | Диапазон принимаемых значений | Пример |
|---|---|---|---|
|
логический тип данных |
|||
| bool | 1 | 0 / 255 | true (истина) или 1...255 / false (ложь) или 0 |
|
символьный тип данных |
|||
| char | 1 | 0 / 255 | char symbol ="a"; или char string[] = "energodar.net"; и т.д. |
| unsigned char | 1 | 0 / 255 | |
| signed char | 1 | -128 / 127 | |
|
целочисленный: int. Варианты: short - укороченный, long - удлинённый, unsigned - без знака. |
|||
| int | 2 | -32 768 / 32 767 | |
| short int | 1 | -128 / 127 | |
| unsigned short int | 1 | 0 / 255 | |
| unsigned int | 2 | 0 / 65 535 | |
| long int | 4 | -2 147 483 648 / 2 147 483 647 | |
| unsigned long int | 4 | 0 / 4 294 967 295 | |
| int8_t | 1 | -128 / 127 | Т.к. в AVR и ESP есть разница, то лучше использовать эти переменные |
| uint8_t | 1 | 0 / 255 | |
| int16_t | 2 | -32 768 / 32 767 | |
| uint16_t | 2 | 0 / 65 535 | |
| int32_t | 4 | -2 147 483 648 / 2 147 483 647 | |
| uint32_t | 4 | 0 / 4 294 967 295 | |
|
типы данных с плавающей точкой |
|||
| float | 4 | -2 147 483 648.0 / 2 147 483 647.0 | |
| long float | 8 | -9 223 372 036 854 775 808 .0 / 9 223 372 036 854 775 807.0 | |
| double | 8 | -9 223 372 036 854 775 808 .0 / 9 223 372 036 854 775 807.0 | |
Подключение библиотек:
#include ... - подключаем СИшный код (библиотеку), написанный до нас для нужных нам функций, например:
#include <avr/io.h> // библиотека ввода вывода
#include <avr/delay.h> // функция задержки: _delay_ms(1000) - задержка 1 секунда
Функции
void main(void){} // main - главная функция микроконтроллеров AVR, которую МК обработает при подаче на него питания. В ней пишем все наши команды
Пользовательские функции
Функция паузы
void pause (unsigned int a) { unsigned int i; for (i=a;i>0;i--); }
Вызываем:
pause(1000);//подождать 1000 тактов цикла for
Циклы
while (1){ //безконечный цикл, т.к. 1 (или любое другое число, отличное от нуля) - это истина
_delay_ms(100); //задержка 100 мс
}
Порты
DDRA - определить порт A на выход (1) или на вход (0), пример:
DDRB=0b00100011; // порт B, ножка PB5 (5-ый бит), PB1 (1-ый бит), PB0 (0-ый бит) будут работать на выход (т.к. = 1), остальные на вход (= 0)
PORTA &=~(1<<5); // сбросить пятый бит порта А, не затрагивая остальные
PORTA = PORTA<<1; // сдвинуть 1 бит, т.е. было, например: 00b00000010, станет 0b00000100
PORTD5 - управление 5-ым битом порта D (ножка PD5)
PORTD |= (1<<(PORTD5)); //5-ый бит порта D в единицу // Это константа определена в файле io.h. Аналогична запись: PORTD |= (1<<5); или
PORTD |= _BV(PD5); //5-ый бит порта D в единицу //_BV - функция побитового сдвига
PORTD &= ~_BV(PD4); //4-ый бит порта D сбросить в 0. Аналогична запись PORTD &= ~(1<<4); или PORTD ^=(1<<PD4);
Проверить пин порта, например, что на RD2 логическая 1:
while (PORTD &(1<<2)) { ... }
if (PORTD &(1<<PD2)) { ... }
Проверить пин порта, например, что на RD2 логический 0:
while (!(PORTD &(1<<2))) { ... }
if (!(PORTD &(1<<PD2))) { ... }
// Пример зажигания светодиода, подключенного к порту RD, третий бит (RD3):
# define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
int main(void){
DDRD |=(1<<3); // RD3 на выход, остальные не трогаем
PORTD &=~(1<<3); // RD3 определяем в 0, остальные не трогаем
while (1) { //вечный цикл
_delay_ms(100); //задержка 100 мс - _delay_ms конечно лучше не использовать, т.е. тратиться машинное время в пустую //PORTD = 0b00000000;
PORTD &=~(1<<3); // RD3 определяем в 0, остальные не трогаем
_delay_ms(100); //задержка 100 мс
PORTD |=(1<<3); // RD3 определяем в 1, остальные не трогаем в отличии, если бы написали так: PORTD = 0b000001000;
}
}
// Пример бегущей точки из светодиодов, зажигаем по очереди порта с 0 по 7
# define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
DDRD = 255; // RD все биты на выход
PORTD = 0b00000000; // RD все биты определяем в 0
int8_t i; // задаём переменную i
i=0; // присваиваем переменной i ноль
while (1) { // вечный цикл
PORTD |=(1<<i); // RD i определяем в 1 (зажигаем)
_delay_ms(1000); //задержка 1000 мс
PORTD &=~(1<<i); // RD i определяем в 0 (т.е. тушим через секунду)
i ; // увеличиваем переменную i на единицу, т.е. переходим к следующему биту порта
if (i==8) i=0; // если i равно 8, то обнуляем, т.к. бита с номером 8 нет (от 0 до 7)
}
}
/* генератор на 8-bit Timer2, CTC режим для AVR ATmega8 */
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
register unsigned char myTimer asm("r28");
ISR(TIMER2_COMP_vect){
myTimer--;
if (!myTimer) {
PORTB ^= (1<<PB5);
myTimer=0x80;
}
}
int main(void){
DDRB |= (1<<PB5); // pinMode(13,OUTPUT); в Wiring
// timer2 setup
TIMSK = (1<<OCIE2); // timer2: compare interrup is enable
TCCR2 = (1<<WGM21); // set CTC mode
TCCR2|= (1<<CS22) |(1<<CS21)| (1<<CS20); // prescaler 1/1024
OCR2 = 0x40;
myTimer=0x80;
sei();
for (;;) {}
return 0;
}
char buf[3];
Temp=543;
buf[2] = Temp % 1000 / 100; // сотни
buf[1] = Temp % 100 / 10; // десятки
buf[0] = Temp % 10; // единицы
// или так:
char buf[3];
Temp=543;
while(Temp>=100){Temp=Temp-100; buf[0] ;} // вычисляем сотни buf[0]==5
while(Temp>=10 ){Temp=Temp-10; buf[1] ;} // вычисляем десятки buf[1]==4
buf[2] = Temp; // единицы buf[2]==3
// для часов:
unsigned long int Temp=10000;//секунды
char buf[4];
while(Temp>=3600){Temp=Temp-3600; buf[0] ;} // вычисляем часы
while(Temp>=600 ){Temp=Temp-600; buf[1] ;} // вычисляем десятки минут
while(Temp>=60 ){Temp=Temp-60; buf[2] ;} // вычисляем единицы минут
while(Temp>=10 ){Temp=Temp-10; buf[3] ;} // вычисляем десятки секунд
buf[4] = Temp; // единицы секунд
sprintf (str, "%s %d %c", "one", 2, '3');
&data; // & - вывести символ по коду, который в переменной data
temp = ((data >> 5) & 0x01);//узнаем значение 5-го бита переменной data
Функции преобразования чисел в разыне системы счисления:
uint8_t tobin(uint8_t bcd) {return ((bcd>>4)*10)+(bcd&15); }//из 16-ти битного(hex) в десятичный bin)
uint8_t tobcd(uint8_t bin) {return ((bin/10)<<4)+(bin%10); }//из bin в hex