Реклама ⓘ
Главная » Микроконтроллеры
Призовой фонд
на апрель 2024 г.
1. 100 руб.
От пользователей

Реклама ⓘ

STM8. Урок 8. Ассемблерный вставки в Cosmic. Сдвиги в Си

Ассемблерные вставки в STM8 для Cosmic

При программировании микроконтроллеров иногда сталкиваются с необходимостью реализации специальных задач, которые реализовать в Си невозможно. Я столкнулся  с реализаций критической секции, где необходимо сохранить регистр состояний (Condition Code register) для фиксации состояние глобального прерывания. Реализовать данную задачу можно только с помощью ассемблерной вставки. Далее будет показана реализация вставки для компилятора Cosmic.

Пример 1.

Реализовать критическую секцию, которая хранит состояние глобального глобального прерывания в регистре состояний. 

uint8_t CondFlag; // Переменная для хранения регистра состояний
void asm_insert(void)
{
	enableInterrupts(); // разрешаем глобальное прерывание
	// начало критической секции
	   _asm("push CC");			// помещаем регистр состояний в стек
       disableInterrupts(); // гарантированно запрещаем глобальное прерывание
	   _asm("pop _cc_reg"); // извлекаем регистр состояний в созданную переменную
	// конец критической секции
	
	GPIO_Init(GPIOE, GPIO_PIN_5, GPIO_MODE_OUT_OD_LOW_FAST); // тестовый код
	// еще какой-то код который должен гарантированно выполняться без прерываний
 // возвращаем исходное состояние регистра состояний, а за одно и прерывания
	#pragma asm			// начало ассемблерной вставки
		push _CondFlag	// помещаем регистра состояния в стек
		pop CC				// загружаем исходное состояние из стека в регистр состояний
	#pragma endasm	// конец ассемблерной вставки
	// конец критической секции
}

Ассемблерные команды вводятся внутри конструкции _asm(" ") или находятся между служебными конструкциями #pragma asm и #pragma endasm, особенно хочется обратить внимание как происходит вставка 8 битных переменных. Для этого необходим в ассемблерной коде перед нашей переменной нужно поставить нижнее подчеркивание "_", т.е. если у нас переменная в Си называется CondFlag, то для использования ее в ассемблере мы пишем _CondFlag, и компилятор вставляет адрес нашей переменной. Прямого доступа к регистру состояний у нас нет, поэтому приходится его читать и писать через стэк. Извлекаем значение регистра в  только после того как гарантированно отключили прерывания, иначе возможно возникновение прерывания в котором тоже используется критическая секция и произойдет перезапись нашей переменной и отловить данную проблему будет очень сложно.

Пример 2.

Приведу еще один пример по работе с 16-битной переменной. В качестве задачи представим, что необходимо реализовать круговой сдвиг битов.

int16_t sdvig=0xa000;
void rotate_left (void)
{
uint8_t i;
for(i=0;i<4;i++) // Повторяем круговой сдвиг влево 4 раза
		{
			#pragma asm			// начало ассемблерной вставки
				LDW X,_sdvig 	// загружаем в регистр Х значение 16-битной переменной по адресу sdvig
				LDW Y,#$7FFF 	// загружаем число для проверки первого бита на 1 или 0
				CPW Y,_sdvig 	// если число больше, чем $7FFF, то бит переноса загружается в бит С регистра СС 
				RLCW X			// производим круговой сдвиг с учетом бита С.
				LDW _sdvig,X	// Загружаем по адресу _sdvig значение из регистра Х
			#pragma endasm	// конец ассемблерной вставки
		}
}

Для понимания работы советую скопировать код и посмотреть как он будет работать. Бит переполнения (Carry) (С) в регистре состояний (Condition Code) я буду называть как в документации (СС.С). 

В строке 8 мы копируем значение sdvig в 16-битный регистр, далее в регистр мы загружаем константу  $7FFF.

В строке 10 производим вычитание из загруженной константы числа, которое мы сдвигаем. Что происходит Если у нас, например, число $8200, то при выполнении команды вычитания из  $7FFF числа $8200 произойдет переполнение и в бит СС.С будет загружена 1, если число меньше или равно $7FFF, то бит СС.С будет равено 0.

В строке 11 при вызове команды RLCW произойдет циклический сдвиг регистра Х, при этом самый старший регистр переместиться в бит СС.С, а текущий бит СС.С в младщий бит регистра Х.

В строке 12 мы загружаем значение из регистра Х по адресу sdvig.

А далее мы все повторяем.

Использование ассемблерных вставок и сопряжение с программой на Си очень простое. 

Арифметические и логические сдвиги в Си

Раз речь зашла про сдвиги хотел бы еще обратить внимание на стандартную операцию сдвига в СИ. Это операция сдвига влево << и сдвига вправо >>. На это обращают мало внимания, но сдвиги делятся на арифметические и логические .В чем разница арифметического и логического сдвига? Арифметический сдвиг - это сдвиг знакового числа, а логический - беззнакового числа. Кроме того, полезно помнить что сдвиг - это быстрое деление или умножение на 2.

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

uint8_t ua=0xCD; // беззнаковая переменная 	исходное		205 или       0b11001101
int8_t  sa=0xCD; // знаковая переменная		исходное		-51 или       0b11001101
uint8_t ua_right; // беззнаковая переменная сдвиг вправо	 51 или     0b00110011
int8_t  sa_right; // знаковая переменная    сдвиг вправо	-13 или     0b11110011
uint16_t ua_left; // беззнаковая переменная сдвиг влево		820 или 0b0000001100110100
int16_t  sa_left; // знаковая переменная	сдвиг влево	   -204 или 0b1111111100110100

ua_right=ua>>2; // результат сдвиг вправо  51 или     0b00110011
sa_right=sa>>2; // результат сдвиг вправо -13 или     0b11110011
ua_left =ua<<2; // результат сдвиг влево  820 или 0b0000001100110100
sa_left =sa<<2; // результат сдвиг влево -204 или 0b1111111100110100 

Как видно, результат операции для сдвига вправо в зависимости от того знаковое или беззнаковое число отличается, но представляет из себя число деленное на 4. Для сдвига влево необходимо расширить диапазон из 8-битного числа перейти к 16-битному, тогда видно что при сдвиге влево происходит умножение на 4, в зависимости от того знаковое или беззнаковое число бинарные результаты отличаются.

Иногда при работе с АЦП в дифференциальном режиме очень удобно выполнять арифметический сдвиг, т.к. встроенный АЦП чаще всего 10-битный и представляет отрицательные число в дополнительном коде для 10-бит, но контроллер работает с дополнительным кодом в 16-битном формате, поэтому далее задача сводится к переводу дополнительный код из 10-битного в 16-битный формат.

Пример. АЦП в дифференциально режиме выдал следующие 10-бит 1.001101101 в дополнительном коде на 10-бит это будет соответствовать -403 для дополнительного кода.

Настройка АЦП с выравниванием влево даст нам 16-битное число a=1.001101101000000, а далее если это число сохраняем в знаковом формате и производим сдвиг вправо на 6 a=a>>6 т.е число станет   1111111.001101101 или -403 в дополнительном коде для 16-битного формата.

Теги:

Опубликована: 0 0
Я собрал 0 1
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 5 Проголосовало: 1 чел.

Комментарии (0) | Я собрал (0) | Подписаться

Статью еще никто не комментировал. Вы можете стать первым.
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется напряжение?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Программатор Pickit3
Программатор Pickit3
Arduino UNO Паяльная станция Hakko 936
вверх