Чт июл 25, 2013 19:54:11
Чт июл 25, 2013 22:20:28
while ((P1IN & BIT3) == 0);
while (!(P1IN & BIT3));
В чём проблема, не подскажете?
Чт июл 25, 2013 22:34:51
YS писал(а):Видимо, в архитектуре взаимодействия двух прерываний.
Чт июл 25, 2013 22:52:08
TA1CCTL0 = CCIE; //разрешаем прерывание по переполнению счётчика-таймера B
Пн июл 29, 2013 14:30:19
Пн июл 29, 2013 18:51:35
Пн июл 29, 2013 20:54:04
TA1CCTL0 = CCIE;
Пн июл 29, 2013 21:39:53
Вт июл 30, 2013 08:55:43
YS писал(а):Отсюда напрашивается вывод: прерывание таймера В вызывается, когда разрешаешь прерывание этого самого таймера. Я не прав?
Сб авг 03, 2013 13:42:17
Сб авг 03, 2013 21:55:39
Можно ли этот лаунчпад использовать тупо как переходник USB - UART?
Сб авг 03, 2013 21:57:03
Вт авг 13, 2013 20:44:42
//используем таймер B для борьбы с дребезгом
TA1CCTL0 = CCIE; //Разрешение прерывания захвата/сравнения счётчика-таймера B/* Timer1_A3 Capture/Compare Control 0 */
TA1CTL = (TASSEL_2 + MC_2 + ID_2); //ставим источник тактов на SMCLK, и режим таймера - continuous, и делитль =4 (1 2 4 8)
//используем таймер B для борьбы с дребезгом
b = true;
TA1CCTL0 = CCIE; //Разрешение прерывания захвата/сравнения счётчика-таймера B/* Timer1_A3 Capture/Compare Control 0 */
TA1CTL = (TASSEL_2 + MC_2 + ID_2); //ставим источник тактов на SMCLK, и режим таймера - continuous, и делитль =4 (1 2 4 8)
b = false;
#pragma vector = TIMER1_A0_VECTOR
__interrupt void TIMER1_A0_PRE (void)
{
if (!(P1IN & BIT3)) return;
if (b==true) return; // вот с этим стало нормально работать :)
P1IE |= BIT3; // Разрешение прерываний на P1.3
TA1CTL = 0; //останов таймера B
}
Пт авг 16, 2013 14:14:44
//******************************************************************************
// Обработка кнопок с определением короткого и длинного нажатия.
// Антидребезг реализован с помощью задержки на нажатие/отпускание кнопки.
// Определение состояния кнопок происходит по прерываниям на портах i/o.
// Длительность нажатия определяется по счетчику в интервальном таймере
// (в данном примере каждые 4ms). Задержка выполнена на строжевом таймере (WDT)
// переводимом на это время в режим интервального таймера.
// Выполнение действий назначенных на кнопки осущесвляется в основном цикле
// программы.
//******************************************************************************
#include "msp430g2553.h"
#include <stdint.h> // Standard integer types
#define LED_1 BIT6 // Зеленый светодиод
#define LED_2 BIT0 // Красный светодиод
#define S1 BIT3 // Кнопка 1
#define S2 BIT4 // Кнопка 2
#define LONG_PRESS_TIME 700 // Продолжительность нажатия клавиши определяемая как "долгое нажатие"
volatile uint8_t Pressed = 0; // Флаг состояния кнопки
volatile uint8_t ButtonPress = 0; // Флаг нажатия кнопки (содержит установленный бит нажатой кнопки)
volatile uint8_t LongPress = 0; // Флаг длинного нажатия кнопки
volatile uint16_t PressCount = 0; // Счетчик времени нажатия
// Обработчик прерывания от PORT1
#pragma vector = PORT1_VECTOR
__interrupt void Port1_ISR(void)
{
uint8_t btn; // Буфер для P1IFG
if(P1IFG & S1 || P1IFG & S2)
{
btn = P1IFG;
P1IE &= ~(S1 + S2); // Запрещаем прерывания от кнопки
WDTCTL = WDT_ADLY_16; // Запуск сторожевого таймера (WDT) в режиме интервального с периодом 48ms
IFG1 &= ~WDTIFG; // Очистка флага прерывания WDT
IE1 |= WDTIE; // Разрешаем прерывания от WDT
if (P1IES & btn)
{ // Если спадающий фронт
Pressed |= btn; // Устанавливаем флаг нажатия кнопки
PressCount = 0; // Сбрасываем счетчик длинного нажатия
}
else
{ // Если нарастающий фронт
if (PressCount < LONG_PRESS_TIME) // Если короткое нажатие на кнопку
{
ButtonPress = btn;
}
Pressed = 0; // Сброс флага состояния кнопки
}
P1IES ^= btn; // Прерывание по нарастающему фронту
P1IFG &= ~(S1 + S2); // Сброс флага прерывания кнопки
btn = 0; // Обнуляем буфер
}
}
// Обработчик прерываний от Timer A1
#pragma vector = TIMER1_A0_VECTOR
__interrupt void TimerA1_ISR(void) {
if (Pressed) // Проверка, нажата ли кнопка
{
if (++PressCount == LONG_PRESS_TIME) // Зафиксировано длительное нажатие 700*4ms = ~3s
{
ButtonPress = Pressed; // Устанавливаем флаг нажатой кнопки
LongPress = 1; // Устанавливаем флаг длительного нажатия кнопки
Pressed = 0; // Сброс флага состояния кнопки
}
}
}
// Обработчик прерываний от таймера WDT
#pragma vector = WDT_VECTOR
__interrupt void WDT_ISR(void)
{
IE1 &= ~WDTIE; // Запрет прерываний от WDT
IFG1 &= ~WDTIFG; // Очистка флага прерывания WDT
WDTCTL = WDTPW + WDTHOLD; // Отключение таймера WDT
P1IFG &= ~(S1 + S2); // Сброс флага прерывания кнопки
P1IE |= S1 + S2; // Разрешаем прерывания от кнопки
}
void main(void) {
WDTCTL = WDTPW + WDTHOLD; // WatchDog off
BCSCTL1 = CALBC1_16MHZ; // 16MHz clock
DCOCTL = CALDCO_16MHZ;
BCSCTL2 |= DIVS_3; // SMCLK = DCO / 8 = 2MHz
BCSCTL3 |= LFXT1S_2; // Set ACLK -> VLOCLK = 12kHz
// *** led ***
P1DIR |= LED_1 + LED_2;
P1OUT &= ~(LED_1 + LED_2);
// *** button ***
P1DIR &= ~(S1 + S2); // Установка вывода кнопки как входного
P1OUT |= S1 + S2; // Подтяжка к питанию
P1REN |= S1 + S2; // Включение подтягивающего резистора
P1IES |= S1 + S2; // Прерывание по спадающему фронту
P1IE |= S1 + S2; // Разрешаем прерывание
P1IFG = 0x00; // Очистка регистра прерываний
// *** Timer1 ***
TA1CTL = TASSEL_2 + ID_3 + MC_1 + TACLR; // SMCLK/8, upmode
TA1CCR0 = 1000; // sample every 4ms
TA1CCTL0 = CCIE; // CCR0 interrupt enabled
__bis_SR_register(GIE); // Разрешаем прерывания
while(1)
{
if (ButtonPress) { // Если была нажата кнопка
switch (ButtonPress & (S1 + S2))
{
case S1: // Нажата S1
if (LongPress) { // Если длинное нажатие
LongPress = 0; // Сбрасываем флаг длительного нажатия
P1OUT ^= LED_2; // Переключаем состояние выхода на противоположное
} else { // Если короткое нажатие
P1OUT ^= LED_1; // Переключаем состояние выхода на противоположное
}
break;
case S2: // Нажата S2
if (LongPress) {
LongPress = 0; // Сбрасываем флаг длительного нажатия
// тут что-то делаем если длинное нажатие
} else {
// тут что-то делаем если короткое нажатие
}
break;
}
ButtonPress = 0; // Сбрасываем флаг нажатия кнопки
}
}
}
//******************************************************************************
// Обработка кнопок с определением короткого и длинного нажатия.
// Антидребезг реализован с помощью задержки на нажатие/отпускание кнопки.
// Определение состояния кнопок происходит по прерываниям на портах i/o.
// Длительность времени задержки и нажатия определяются по счетчикам
// в интервальном таймере (в данном примере каждые 4ms).
// Выполнение действий назначенных на кнопки осущесвляется в основном цикле
// программы.
//******************************************************************************
#include "msp430g2553.h"
#include <stdint.h> // Standard integer types
#define LED_1 BIT6 // Зеленый светодиод
#define LED_2 BIT0 // Красный светодиод
#define S1 BIT3 // Кнопка 1
#define S2 BIT4 // Кнопка 2
#define LONG_PRESS_TIME 700 // Продолжительность нажатия клавиши определяемая как "долгое нажатие"
#define LOCK_PRESS_TIME 10 // Продолжительность блокироки нажатия кнопки
volatile uint8_t Pressed = 0; // Флаг состояния кнопки
volatile uint8_t ButtonPress = 0; // Флаг нажатия кнопки (содержит установленный бит нажатой кнопки)
volatile uint8_t LongPress = 0; // Флаг длинного нажатия кнопки
volatile uint8_t LockPress = 0; // Флаг блокировки нажатия кнопки
volatile uint8_t LockPressCount = 0; // Счетчик времени блокировки нажатия
volatile uint16_t PressCount = 0; // Счетчик времени нажатия
// Обработчик прерывания от PORT1
#pragma vector = PORT1_VECTOR
__interrupt void Port1_ISR(void)
{
uint8_t btn; // Буфер для P1IFG
if(P1IFG & S1 || P1IFG & S2)
{
btn = P1IFG; // Состояние регистра P1IFG в буфер
P1IE &= ~(S1 + S2); // Запрещаем прерывания от кнопки
LockPressCount = 0; // Обнуляем счетчик времени блокировки кнопки
LockPress = 1; // Устанавливаем флаг блокировки нажатия клавиши
if (P1IES & btn)
{ // Если спадающий фронт
Pressed |= btn; // Устанавливаем флаг нажатия кнопки
PressCount = 0; // Сбрасываем счетчик длинного нажатия
}
else
{ // Если нарастающий фронт
if (PressCount < LONG_PRESS_TIME) // Если короткое нажатие на кнопку
{
ButtonPress = btn;
}
Pressed = 0; // Сброс флага состояния кнопки
}
P1IES ^= btn; // Прерывание по нарастающему фронту
P1IFG &= ~(S1 + S2); // Сброс флага прерывания кнопки
btn = 0; // Обнуляем буфер
}
}
// Обработчик прерываний от Timer A1
#pragma vector = TIMER1_A0_VECTOR
__interrupt void TimerA1_ISR(void) {
if (LockPress) // Если установлен флаг блокировки кнопки
{
if (++LockPressCount == LOCK_PRESS_TIME) // Если время блокировки истекло (10*4ms = 40ms)
{
P1IFG &= ~(S1 + S2); // Сброс флага прерывания кнопки
P1IE |= S1 + S2; // Разрешаем прерывания от кнопки
LockPress = 0; // Сбрасываем флаг блокировки нажатия кнопки
}
}
if (Pressed) // Проверка, нажата ли кнопка
{
if (++PressCount == LONG_PRESS_TIME) // Зафиксировано длительное нажатие 700*4ms = ~3s
{
ButtonPress = Pressed; // Устанавливаем флаг нажатой кнопки
LongPress = 1; // Устанавливаем флаг длительного нажатия кнопки
Pressed = 0; // Сброс флага состояния кнопки
}
}
}
void main(void) {
WDTCTL = WDTPW + WDTHOLD; // WatchDog off
BCSCTL1 = CALBC1_16MHZ; // 16MHz clock
DCOCTL = CALDCO_16MHZ;
BCSCTL2 |= DIVS_3; // SMCLK = DCO / 8 = 2MHz
// *** led ***
P1DIR |= LED_1 + LED_2;
P1OUT &= ~(LED_1 + LED_2);
// *** button ***
P1DIR &= ~(S1 + S2); // Установка вывода кнопки как входного
P1OUT |= S1 + S2; // Подтяжка к питанию
P1REN |= S1 + S2; // Включение подтягивающего резистора
P1IES |= S1 + S2; // Прерывание по спадающему фронту
P1IE |= S1 + S2; // Разрешаем прерывание
P1IFG = 0x00; // Очистка регистра прерываний
// *** Timer1 ***
TA1CTL = TASSEL_2 + ID_3 + MC_1 + TACLR; // SMCLK/8, upmode
TA1CCR0 = 1000; // sample every 4ms
TA1CCTL0 = CCIE; // CCR0 interrupt enabled
__bis_SR_register(GIE); // Разрешаем прерывания
while(1)
{
if (ButtonPress) { // Если была нажата кнопка
switch (ButtonPress & (S1 + S2))
{
case S1: // Нажата S1
if (LongPress) { // Если длинное нажатие
LongPress = 0; // Сбрасываем флаг длительного нажатия
P1OUT ^= LED_2; // Переключаем состояние выхода на противоположное
} else { // Если короткое нажатие
P1OUT ^= LED_1; // Переключаем состояние выхода на противоположное
}
break;
case S2: // Нажата S2
if (LongPress) {
LongPress = 0; // Сбрасываем флаг длительного нажатия
// тут что-то делаем если длинное нажатие
} else {
// тут что-то делаем если короткое нажатие
}
break;
}
ButtonPress = 0; // Сбрасываем флаг нажатия кнопки
}
}
}
//******************************************************************************
// Обработка кнопок с определением короткого и длинного нажатия.
// Антидребезг реализован с помощью сдвигового регистра путем сравнения
// нескольких последовательных состояний. Определение состояния кнопок
// происходит по прерыванию интервального таймера (в данном примере каждые 4ms).
// Выполнение действий назначенных на кнопки осущесвляется в основном цикле
// программы.
//******************************************************************************
#include "msp430g2553.h"
#include <stdint.h> // Standard integer types
#define LED_1 BIT6 // Зеленый светодиод
#define LED_2 BIT0 // Красный светодиод
#define S1 BIT3 // Кнопка 1
#define S2 BIT4 // Кнопка 2
// Константы для сравнения состояния сдвигового регистра
#define PRESS_THRESHOLD 0x3F // Пороговое значение регистра при нажатии кнопки
#define RELEASE_THRESHOLD 0xFC // Пороговое значение регистра при отпускании кнопки
#define LONG_PRESS_TIME 700 // Время определяемое как длительное нажатие кнопки (700*4ms = ~ 3s)
uint8_t LongPress = 0; // флаг длительного нажатия кнопки
volatile uint8_t ButtonPress = 0; // Бит нажатой кнопки
// - - - - - - - - - - - - - - - - - - - - -
// PRESS_THRESHOLD = 0x3F = 0 b00111111
// RELEASE_THRESHOLD = 0xFC = 0 b11111100
// - - - - - - - - - - - - - - - - - - - - -
#pragma vector = TIMER1_A0_VECTOR
__interrupt void Timer1_A(void)
{
uint8_t BtnMask = 0; // Бит нажатой кнопки
static uint16_t PressCount = 0; // Время нажатия кнопки
static uint8_t BtnState = 0; // Флаг устйчивого состояния кнопки (после дребезга)
static uint8_t Pressed = 0; // Флаг - кнопка была нажата
static uint8_t ShiftReg = 0xFF; // Сдвиговый регистр для хранения состояния кнопки
ShiftReg >>= 1; // Сдвигаем содержимое регистра на 1 вправо
if (!(P1IN & S1)) BtnMask = S1; // Опрос входов кнопок
if (!(P1IN & S2)) BtnMask = S2;
if (!BtnMask) { // Если ни одна кнопка не нажата (или пропал контакт во время дребезга)
ShiftReg |= BIT7; // Устанавливаем в 1 старший разряд сдвигового регистра
}
if (BtnState == 1) { // Проверяем статус кнопки - если нажата
if (!(PressCount >= LONG_PRESS_TIME)){ // Если счетчик уже досчитал, то ничего не деламе, иначе...
if (++PressCount >= LONG_PRESS_TIME) { // Увеличиваем счетчик и если досчитал
LongPress = 1; // Устанавливаем флаг длинного нажатия
ButtonPress = Pressed; // И бит нажатой кнопки
}
}
// Проверяем не отжата ли кнопка
if (ShiftReg >= RELEASE_THRESHOLD) { // Если кнопка отжата
BtnState = 0; // Устанавливаем статус кнопки в отжата (0)
}
} else { // Если статус кнопки - отжата
if (Pressed) { // Но было зафиксировано нажатие
if (PressCount < LONG_PRESS_TIME) { // И если время нажатия меньше чем заданное как длительное
LongPress = 0; // Сбрасываем флаг длинного нажатия
ButtonPress = Pressed; // Сохраняем бит нажатой кнопки
}
PressCount = 0; // Обнуляем счетчик длинного нажатия
Pressed = 0; // Обнуляем флаг нажатой кнопки
}
// Проверяем не нажата ли кнопка
if (ShiftReg <= PRESS_THRESHOLD) { // Если кнопка нажата
BtnState = 1; // Устанавливаем статус кнопки в нажата (1)
Pressed = BtnMask; // Сохраняем бит нажатой кнопки
}
}
}
void main (void)
{
WDTCTL = WDTPW | WDTHOLD; // Останавливаем сторожевой таймер
BCSCTL1 = CALBC1_16MHZ; // 16MHz clock
DCOCTL = CALDCO_16MHZ;
BCSCTL2 |= DIVS_3; // SMCLK = DCO / 8 = 2MHz
// *** led ***
P1DIR |= LED_1 + LED_2;
P1OUT &= ~(LED_1 + LED_2);
// *** button ***
P1DIR &= ~(S1 + S2); // Установка вывода кнопки как входного
P1OUT |= S1 + S2; // Подтяжка к питанию
P1REN |= S1 + S2; // Включение подтягивающего резистора
// *** Timer1 ***
TA1CTL = TASSEL_2 + ID_3 + MC_1 + TACLR; // SMCLK/8, upmode
TA1CCR0 = 1000; // sample every 4ms
TA1CCTL0 = CCIE; // CCR0 interrupt enabled
__bis_SR_register(GIE); // Разрешаем прерывания
while(1)
{
if (ButtonPress) { // Если была нажата кнопка
switch (ButtonPress & (S1 + S2))
{
case S1: // Нажата S1
if (LongPress) { // Если длинное нажатие
P1OUT ^= LED_2; // Переключаем состояние выхода на противоположное
} else { // Если короткое нажатие
P1OUT ^= LED_1; // Переключаем состояние выхода на противоположное
}
break;
case S2: // Нажата S2
if (LongPress) {
} else {
}
break;
}
ButtonPress = 0; // Сбрасываем флаг нажатия кнопки
}
}
}
Пт авг 16, 2013 18:29:59
Сб авг 17, 2013 07:18:51
//******************************************************************************
//
// Пример работы с кнопками
//
// Определение короткого и длинного нажатия. Антидребезг реализован с помощью
// задержек на нажатие/отпускание кнопок.
// Организовать вызов из прерывания с частотой 100Гц - функцию BtnExe();
// Чтение значения состояния флагов кнопок производится с помощью
// глобальной переменной BtnFlags например так (в главном цикле):
// if (BtnFlags) {
// if (BtnMask & BTN_SHRT_S1) {....ветка короткого нажатия кнопки S1}
// if (BtnMask & BTN_SHRT_S2) {....ветка короткого нажатия кнопки S1}
// if (BtnMask & BTN_LONG_S1) {....ветка длинного нажатия кнопки S1}
// и т.д.
// BtnFlags = 0 //Обнулить байт флагов нажатия кнопок
// }
//
//******************************************************************************
#include "msp430g2553.h"
#include <stdint.h> // Standard integer types
#define LED_1 BIT6 // Зеленый светодиод
#define LED_2 BIT0 // Красный светодиод
#define S1 BIT3 // Кнопка 1
#define S2 BIT4 // Кнопка 2
//настройка параметров работы функций
#define BTN_LOCK_TIME 30 //время обработки дребезга в милисекундах (10-100)
#define BTN_LONG_TIME 1000 //время фиксации длинного нажатия в милисекундах (1000 - 2500)
//глобальные переменные
volatile uint8_t BtnFlags; //байт флагов нажатия кнопки
#define BTN_SHRT_S1 (1<<0) //бит короткого нажатия кнопки S1
#define BTN_SHRT_S2 (1<<1) //бит короткого нажатия кнопки S2
#define BTN_LONG_S1 (1<<2) //бит длинного нажатия кнопки S1
#define BTN_LONG_S2 (1<<3) //бит длинного нажатия кнопки S2
void BtnExe (void);
// Обработчик прерываний от Timer A1
#pragma vector = TIMER1_A0_VECTOR
__interrupt void TimerA1_ISR(void) {
BtnExe();
}
void main(void) {
WDTCTL = WDTPW + WDTHOLD; // WatchDog off
BCSCTL1 = CALBC1_1MHZ; // 1MHz clock
DCOCTL = CALDCO_1MHZ;
// *** led ***
P1DIR |= LED_1 + LED_2;
P1OUT &= ~(LED_1 + LED_2);
// *** button ***
P1DIR &= ~(S1 + S2); // Установка вывода кнопки как входного
P1OUT |= S1 + S2; // Подтяжка к питанию
P1REN |= S1 + S2; // Включение подтягивающего резистора
// *** Timer1 ***
TA1CTL = TASSEL_2 + ID_3 + MC_1 + TACLR; // SMCLK/8, upmode
TA1CCR0 = 2000; // sample every 10ms
TA1CCTL0 = CCIE; // CCR0 interrupt enabled
__bis_SR_register(GIE); // Разрешаем прерывания
while(1)
{
if (BtnFlags) { // Если была нажата кнопка
if (BtnFlags & BTN_SHRT_S1) { // Ветка короткого нажатия кнопки S1
P1OUT ^= LED_1; // Переключаем состояние выхода на противоположное
}
if (BtnFlags & BTN_SHRT_S2) { // Ветка короткого нажатия кнопки S2
}
if (BtnFlags & BTN_LONG_S1) { // Ветка длинного нажатия кнопки S1
P1OUT ^= LED_2; // Переключаем состояние выхода на противоположное
}
if (BtnFlags & BTN_LONG_S2) { // Ветка длинного нажатия кнопки S2
}
BtnFlags = 0;
}
}
}
//----------
//ФУНКЦИЯ ОБРАБОТКИ НАЖАТИЙ КЛАВИШ (вызывать в прерывании с частотой 100 Гц)
//короткое нажатие устанавливает бит BTN_SHRT_X глобальной переменной BtnFlags
//длинное нажатие устанавливает бит BTN_LONG_X глобальной переменной BtnFlags
void BtnExe (void)
{
static uint8_t BtnLockBit; //защелка (защита от дребезга)
static uint8_t BtnLockCoun; //счетчик защелки (защита от дребезга)
static uint8_t BtnLongCoun; //счетчик длинного нажатия
static uint8_t BtnLastState; //последнее состояние кнопок перед отпусканием
uint8_t mask = 0;
if (!(P1IN & S1)) mask = BTN_SHRT_S1;
if (!(P1IN & S2)) mask = BTN_SHRT_S2;
if (mask){ //опрос состояния кнопки
if (BtnLockCoun < (BTN_LOCK_TIME/10)){ //клавиша нажата
BtnLockCoun++;
return; //защелка еще не дощитала - возврат
}
BtnLastState = mask;
BtnLockBit =1; //нажатие зафиксировано
if (BtnLongCoun >= (BTN_LONG_TIME/10))
return; //возврат, т.к. счетчик длинн нажат досчитал до максимума еще раньше
if (++BtnLongCoun >= (BTN_LONG_TIME/10))
BtnFlags |= (BtnLastState<<2); //счетчик досчитал до максимума - устанавливаем биты длинного нажатия
}
else{ //клавиша отжата
if (BtnLockCoun){
BtnLockCoun --;
return; //защелка еще не обнулилась - возврат
}
if (! BtnLockBit) //СТАТИЧЕСКИЙ ВОЗВРАТ
return;
BtnLockBit =0; //отжатие зафиксировано
if (BtnLongCoun < (BTN_LONG_TIME/10))
BtnFlags |= BtnLastState; //установка бита короткого нажатия
BtnLongCoun = 0; //сброс счетчика длительности нажатия
}
}
Сб авг 17, 2013 19:43:58
Вс авг 18, 2013 11:34:07
Ser60 писал(а):Сейчас заканчиваю проект с участием кнопок, где вышеупомянутый алгоритм адаптирован с FR57xx для MSP430G2332
Пн авг 19, 2013 08:05:26
Bujhm666 писал(а):просто вместо обычных здесь пиков или меги управление от MSP430
Пн авг 19, 2013 10:29:55
Приложения, нуждающиеся в генерации периодических колебаний, могут получить преимущества от использования контроллера DMA с ЦАП12. К примеру, приложение, вырабатывающее синусоидальное колебание может сохранит значения синуса в таблице. Контроллер DMA может непрерывно автоматически переносить эти значения в ЦАП12 через заданные интервалы, создавая синусоиду без участия ЦПУ