- •Отчет по выполнению лабораторных работ
- •2. Использование таймеров для генерации временных интервалов и тональных сигналов
- •3. Характеристики аналого-цифрового преобразователя
- •4. Генерация сигнала методом прямого цифрового синтеза. Быстрое преобразование Фурье (бпф)
- •4.1. Проверка работы генератора гармонического сигнала и аналого-цифрового преобразования.
- •4.2. Исследование особенностей бпф.
- •4.3. Использование оконной функции.
Отчет по выполнению лабораторных работ
по дисциплине «Сигнальные процессоры»
Лабораторная работа № 2
Периферийные модули процессоров ARMCortex-M4 и их использование при вводе, выводе, обработке сигналов
Выполнил студент: Дата выполнения:
1. Высокоскоростная передача через порты общего назначения.
|
Вариант передачи | ||
Процедура на языке Си |
Процедура на языке ассемблера |
Прямой доступ к памяти (DMA) | |
Число изменений контрольной индикации за … с |
38 |
84 |
106 |
Частота контрольной индикации, Гц |
0.76 |
1.68 |
2.12 |
Число циклов вывода за период изменения индикации |
10240000 |
10240000 |
10240000 |
Частота вывода, МГц |
7.7824 |
17.2032 |
21.7088 |
Скорость вывода, Мбайт/с |
15.5648 |
34.4064 |
43.4176 |
|
|
|
|
Число процессорных тактов между выводами |
21.59 |
9.77 |
7.74 |
Число исполняемых команд между выводами |
- |
- |
– |
При наличии осциллографических измерений: | |||
Период вывода кодов, нс |
120 |
60 |
44 |
Скорость вывода, Мбайт/с |
16.67 |
33.3 |
45.45 |
Выводы
Язык Си-маленькая скорость передачи
Ассемблер-Скорость выше чем на Си
Прямой доступ-превосходит в скорости оба варианта
Домашнее задание
Скорректировать программу с целью реализации передачи информации с заданной скоростью: Кбайт/с по одному из программных вариантов и варианту с DMA.
Текст программы
Исходный модуль D:\ARM\Work\DspLab_2_1\main.c
//---------------------------------------------------------------------------
//
// УЧЕБНЫЙ ПРОЕКТ
// Высокоскоростной ввод-вывод через порты общего назначения (параллельный интерфейс)
// Copyright (C) 2013 МГТУ МИРЭА
//
//---------------------------------------------------------------------------
// Заголовочные файлы
#include "stm32_p407.h" //Файл конфигурации отладочной платы STM32-P407
#include "values.h" //Числовые значения исходных операндов
//Прототипы функций, определенных в этом файле
void PortEInitialize(void);
__asm void BufferToOutput(GPIO_TypeDef* GPIOx, uint16_t* data_pointer, uint32_t number);
void DMAInitialize(void);
//Глобальные параметры
uint16_t MemoryBuffer[BUFLENGTH]; //Буфер выводимых данных
const int CyclesNumber = CYCLESNUM; //Число циклов вывода буфера (константа)
volatile int CyclesCounter; //Счетчик числа циклов вывода буфера - используется
// только в режиме DMA
//---------------------------------------------------------------------------
// ГЛАВНАЯ ФУНКЦИЯ
int main()
{
//Текущие переменные
int i, j; //Переменные цикла
uint16_t* pMemBuffer; //Указатель на буфер
//Инициализация органов управления
STM_PBInit(BUTTON_DOWN, BUTTON_MODE_GPIO); //Позиция джойстика "вниз"
STM_PBInit(BUTTON_UP, BUTTON_MODE_GPIO); //Позиция джойстика "вверх"
STM_PBInit(BUTTON_WAKEUP, BUTTON_MODE_GPIO); //Кнопка WAKEUP выхода из программы
//Инициализация светодиодных индикаторов
STM_LEDInit(LED3); //"Красный"
STM_LEDInit(LED4); //"Зеленый"
//Программирование порта для вывода данных
PortEInitialize();
//Конфигурирование режима прямого доступа к памяти
DMAInitialize();
//Заполнение буфера отладочными кодами
for (i = 0; i < sizeof(MemoryBuffer)/4; i++) *((uint32_t*)MemoryBuffer + i) = 0x5555AAAA;
//Цикл основной программы
while (1)
{
if (STM_PBGetState(BUTTON_DOWN)) //Если джойстик в положении "вниз"
//Передача с использованием подпрограммы на языке Си
for (j = 0; j < CyclesNumber; j++) //Внешний цикл повторений передачи буфера
{ pMemBuffer = MemoryBuffer; //Указатель - на начало буфера
i = sizeof(MemoryBuffer)/2; //Число выводимых кодов
while (--i) GPIO_Write(GPIOE, *pMemBuffer++); //Внутренний цикл передачи буфера
}
else
if (!STM_PBGetState(BUTTON_UP)) //Если джойстик в исходном состоянии
//Передача с использованием подпрограммы на языке ассемблера
for (j = 0; j < CyclesNumber; j++) //Внешний цикл повторений передачи буфера
{ pMemBuffer = MemoryBuffer; //Указатель - на начало буфера
i = sizeof(MemoryBuffer)/2; //Число выводимых кодов
BufferToOutput(GPIOE, pMemBuffer, i); //Внутренний цикл передачи буфера
}
else //Джойстик в положении "вверх"
//Передача с использованием режима прямого доступа к памяти
{ CyclesCounter = 0; //Обнуление счетчика числа повторений передачи
TIM_Cmd(TIM8, ENABLE); //Разрешение обмена
while (CyclesCounter < CyclesNumber) __WFI(); //Ожидание (в спящем режиме) окончания передачи
TIM_Cmd(TIM8, DISABLE); //Остановка обмена
}
//Переключение индикации
STM_LEDToggle(LED4);
//Проверка нажатия кнопки WAKEUP, если нажата, сброс процессора
if (STM_PBGetState(BUTTON_WAKEUP)) NVIC_SystemReset();
}
}
//---------------------------------------------------------------------------
// ПОДПРОГРАММА ВЫВОДА БУФЕРА (на языке ассемблера)
// При вызове подпрограммы входные параметры загружаются соответственно в регистры R0, R1, R2,
// адрес возврата записан в регистре LR
__asm void BufferToOutput( //Входные параметры:
GPIO_TypeDef* GPIOx, //Базовый адрес порта (R0)
uint16_t* data_pointer, //Указатель на выводимые данные (R1)
uint32_t number) //Число циклов вывода (R2)
{
label
LDRH R3,[R1],#0x02 //Чтение полуслова из памяти с адресом в R1 в регистр R2
// с последующим увеличением содержимого R1 на 2
STR R3,[R0,#0x14] //Пересылка из регистра R3 по адресу (R0 + 0x14) -
// адресу выходного регистра порта
SUBS R2,R2,#1 //Уменьшение содержимого регистра R2 на 1 с установкой признаков
BNE label //Если не 0, переход на команду с меткой label
BX LR //Возврат в вызывающую программу по адресу в регистре связи LR
}
//---------------------------------------------------------------------------
// ПРОГРАММИРОВАНИЕ ПОРТА ОБЩЕГО НАЗНАЧЕНИЯ PE
void PortEInitialize(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //Структура конфигурации портов общего назначения
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); //Разрешение тактирования порта
GPIO_InitStructure.GPIO_Pin = 0xFF87; //Маска на выходные разряды (разряды 3..6 не исп.)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //Режим: вывод
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //Задание быстродействия (2MHz, 25MHz, 50MHz, 100MHz)
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //Установка типа выходного каскада: двухтактный
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //Подтягивающий резистор: нет
GPIO_Init(GPIOE, &GPIO_InitStructure); //Функция конфигурирования
}
//---------------------------------------------------------------------------
// КОНФИГУРИРОВАНИЕ ВЫВОДА В ПОРТ В РЕЖИМЕ ПРЯМОГО ДОСТУПА К ПАМЯТИ
// Каждая транзакция (передача кода) инициируется событием перезагрузки таймера.
// Общий старт и остановка процесса осуществляются в основной программе.
// Для подсчета числа передач задействовано прерывание, возникающее по окончании
// передачи всего буфера.
void DMAInitialize(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //Структура конфигурации базового таймера
DMA_InitTypeDef DMA_InitStructure; //Структура конфигурации канала DMA
NVIC_InitTypeDef NVIC_InitStructure; //Структура конфигурации прерываний
//Конфигурирование таймера - источника запуска DMA-транзакций
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE); //Разрешение тактирования таймера
TIM_TimeBaseStructure.TIM_Period = 4; //Основной коэф. деления
TIM_TimeBaseStructure.TIM_Prescaler = 0; //Коэффициент предделения
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //Делитель для входного фильтра (не используется)
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //Режим счета вверх: от 0 до TIM_Period
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //Счетчик повторов (не используется)
TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure); //Функция конфигурирования
//Конфигурирование канала DMA
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); //Разрешение тактирования контроллера DMA
DMA_DeInit(DMA2_Stream1); //Начальная установка (сброс) канала
DMA_InitStructure.DMA_Channel = DMA_Channel_7; //Номер источника запроса для канала
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&GPIOE->ODR); //Адрес периферийного устройства (порта)
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)MemoryBuffer; //Адрес буфера памяти
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //Направление передачи: память -> периферия
DMA_InitStructure.DMA_BufferSize = sizeof(MemoryBuffer)/2; //Число транзакций для передачи всего буфера
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Автоувеличение адреса периферии: нет
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Автоувеличение адреса памяти: да
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //Размер транзакции для памяти: полуслово
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord; //Размер транзакции для периферии: полуслово
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //Режим передачи буфера: циклический
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; //Уровень приоритета: наивысший
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //Использование промежуточного FIFO: нет
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; //Порог для FIFO (здесь не используется)
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //Размер пакета для памяти: одиночный
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //Размер пакета для периферии: одиночный
DMA_Init(DMA2_Stream1, &DMA_InitStructure); //Функция конфигурирования
TIM_DMACmd(TIM8, TIM_DMA_Update, ENABLE); //Выбор источника и вида запуска: таймер 8
// и событие его перезагрузки
DMA_Cmd(DMA2_Stream1, ENABLE); //Разрешение работы канала DMA
//Конфигурирование прерывания по окончании DMA (обработчик: DMA2_Stream1_IRQHandler)
DMA_ITConfig(DMA2_Stream1, DMA_IT_TC, ENABLE); //Тип прерывания от DMA: окончание передачи буфера
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream1_IRQn; //Номер источника запроса прерывания
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4; //Уровень приоритета (0 - высший, 15 - низший)
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //Приоритет внутри группы
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //Разрешение прерывания
NVIC_Init(&NVIC_InitStructure); //Функция конфигурирования
}
//---------------------------------------------------------------------------
// ОБСЛУЖИВАНИЕ ПРЕРЫВАНИЯ ОТ КОНТРОЛЛЕРА DMA ПО ЗАВЕРШЕНИИ ПЕРЕДАЧИ БУФЕРА
void DMA2_Stream1_IRQHandler(void)
{
DMA_ClearITPendingBit(DMA2_Stream1, DMA_IT_TCIF1); //Очистка флага запроса
CyclesCounter++; //Увеличение счетчика числа повторов передачи буфера
} // (проверяется в основной программе)
//---------------------------------------------------------------------------