Лабораторная работа № 2 (п.п. 2, 4)

ОТЧЕТ ПО ВЫПОЛНЕНИЮ ЛАБОРАТОРНЫХ РАБОТ

По дисциплине «Цифровая обработка телекоммуникационных сигналов»

Лабораторная работа № 1 (п.п. 1, 3)

Вычислительные и графические операции в процессорах
с ядром ARM Cortex-M4

 

Выполнил студент:                                             Дата выполнения:

 

 

1. Ознакомление со структурой проекта. Арифметические операции с данными ограниченной разрядности.

 

Выполняется в лаборатории

Формат, система счисления Элемент 0 Элемент 1 Элемент 2 Элемент 3
Целый без знака, 16-ричная        
Целый со знаком, 10-чная        
Дробный 1.15, 10-чная          

 

 

Выполняется при оформлении отчета

  Элемент 0 Элемент 1
Операции с 16-разрядными двоичными кодами    
Наличие переноса    
Наличие переполнения    
  Элемент 2 Элемент 3
Операции с 16-разрядными двоичными кодами    
Наличие переноса    
Наличие переполнения    

 

 

Выводы

 

Причина получения некорректных результатов: ...

 

 

Признак переполнения устанавливается при операциях сложения/вычитания со знаковыми числами, когда ...

· ... (охарактеризовать диапазон) ...

· ... (охарактеризовать битовые соотношения) ...

 

 

Признак переноса устанавливается при операциях сложения/вычитания с числами без знака, когда ...

· ...

· ...

 

Текст программы

 

Внимание! Привести скорректированный текст. Изменения/добавления выделить жирным шрифтом. Использовать моноширинный шрифт (Courier, Consolas и т.п.)

 

Исходный модуль ..\Dsp1_arithm\main.c

//---------------------------------------------------------------------------

//

// УЧЕБНЫЙ ПРОЕКТ

// Арифметические операции с 16-разрядными данных

// Copyright (C) 2017 МИРЭА

//

//---------------------------------------------------------------------------

// Справка:

//  ОСНОВНЫЕ ТИПЫ ДАННЫХ

// Стандартные        Компилятора

// языка C:           ARM:

// ------------       --------

// signed char        int8_t    8-разрядное знаковое целое (-128...127)

// signed short       int16_t   16-разрядное знаковое целое (-32768...32767)

// signed long        int32_t   32-разрядное знаковое целое (-2147483648...2147483647)

// signed long long   int64_t   64-разрядное знаковое целое

//                                            (-9223372036854775808...9223372036854775807)

// unsigned char      uint8_t   8-разрядное беззнаковое целое (0...255)

// unsigned short     uint16_t  16-разрядное беззнаковое целое (0...65535)

// unsigned long      uint32_t  32-разрядное беззнаковое целое (0...4294967295)

// unsigned long long uint64_t  64-разрядное беззнаковое целое (0...18446744073709551615)

// float              float32_t 32-разрядное с плавающей точкой

// double             float64_t 64-разрядное с плавающей точкой

// signed char        q7_t      8-разрядное дробное в формате 1.7

// signed short       q15_t     16-разрядное дробное в формате 1.15

// signed long        q31_t     32-разрядное дробное в формате 1.31

// signed long long   q63_t     64-разрядное дробное в формате 1.63

// int                               Для компилятора ARM соответствует типу long

// bool                              Булево (0 и 1)

//

//---------------------------------------------------------------------------

 

// Заголовочные (подключаемые) файлы

#include "stm32_p407.h"            //Файл конфигурации отладочной платы STM32-P407

#include "lcd.h"                   //Функции для работы с дисплеем

#include "values.h"                //Числовые значения исходных операндов

 

// Значения операндов: определены в подключаемом файле

// Внимание! На разных рабочих местах используются разные значения

int16_t X[] = { valueX0, valueX1, valueX2, valueX3 };

int16_t Y[] = { valueY0, valueY1, valueY2, valueY3 };

 

//---------------------------------------------------------------------------

// ГЛАВНАЯ ФУНКЦИЯ

int main()

{

//Текущие (локальные) переменные

int i = 0;                                //Индекс элемента массива

uint32_t fract = 0x8000;                  //Делитель для преобразования целого в дробное

int16_t res;                              //Результат сложения

 

//Инициализация кнопок (для режима программного опроса их состояния)

STM_PBInit(BUTTON_TAMPER, BUTTON_MODE_GPIO); //Кнопка TAMPER шагового выполнения

STM_PBInit(BUTTON_WAKEUP, BUTTON_MODE_GPIO); //Кнопка WAKEUP выхода из программы

 

//Инициализация светодиодных индикаторов

STM_LEDInit(LED3); STM_LEDInit(LED4);

 

//Инициализация и вывод сообщения на дисплей

LCD_Init();                               //Инициализация интерфейса и режима работы дисплея

LCD_Clear(0);                             //Очистка дисплея

LCD_FontSize(11);                         //Установка размера шрифта

LCD_print(" Для выполнения \r\n"          //Начальная заставка

       " операций \r\n"

       " по шагам \r\n"

       " нажимайте \r\n"

       " \"TAMPER\" \r\n\r\n"

       "Выход - \"WAKEUP\"");

LCD_FontSize(9);

           

 

//Начало бесконечного цикла в программе

while (1)

{

if (!STM_PBGetState(BUTTON_TAMPER))     //Проверка нажатия кнопки TAMPER:

{                                       // если нажата, то вход в этот блок {}

while (!STM_PBGetState(BUTTON_TAMPER)); //Ожидание отпускания кнопки TAMPER

   

res = X[i] + Y[i];                    //Операция сложения 16-разрядных операндов

LCD_Clear(0);                         //Очистка дисплея

LCD_print("Элемент %d:\r\n\r\n"       //Вывод исходных данных и результата

           "0x%X + 0x%X =\r\n 0x%X \r\n\r\n"

           "%d + %d =\r\n %d \r\n\r\n"

           "%.5f + %.5f =\r\n %.5f \r\n",

            i, (uint16_t)X[i], (uint16_t)Y[i], (uint16_t)res,

            X[i], Y[i], res,

            (float)X[i] / fract, (float)Y[i] / fract, (float)res / fract);

                                            //Переключение индикаторов на каждом шаге

if (i & 1) STM_LEDOff(LED3), STM_LEDOff(LED4);

else STM_LEDOn(LED3), STM_LEDOn(LED4);

                

i++;                                  //Увеличение индекса элемента

if (i >= 4) i = 0;                    //Если обработано 4 элемента, переход к начальному

}

                                            //Здесь кнопка TAMPER не нажата

if (STM_PBGetState(BUTTON_WAKEUP)) break; //Проверка нажатия кнопки WAKEUP, если нажата,

}                                         // отладочный выход из цикла while (1)

 

//Сброс процессора - завершение выполнения данной программы, запуск начального загрузчика

NVIC_SystemReset();

}

 

//---------------------------------------------------------------------------

 


3. Построение графиков функций

 

Исходные диаграммы, полученные в среде разработки

 

 

Исходные диаграммы на дисплее отладочной платы и диаграммы в режиме насыщения:

 

Текст программы

 

Внимание! Привести скорректированный текст. Изменения/добавления выделить другим шрифтом (цветом, фоном) или иным способом.

 

Исходный модуль ..\Dsp1_graph\main.c

//---------------------------------------------------------------------------

//

// УЧЕБНЫЙ ПРОЕКТ

// ПОСТРОЕНИЕ ГРАФИКОВ ФУНКЦИЙ.

// Графические построения: на дисплее отладочной платы,

//                     в режиме симуляции - средствами среды.

// Copyright (C) 2017 МИРЭА

//

//---------------------------------------------------------------------------

/* 

ОСНОВНЫЕ ОПЕРАЦИИ

 

Компиляция проекта:      клавиша F7 (кнопка Build или меню Project/Build)

Вход в режим отладки:    клавиша Ctrl+F5 (кнопка или меню Debug/Start/Stop Debug Session)

Вставка точки останова:  клавиша F9 (кнопка или меню Debug/Insert/Remove Breakpoint)

Шаговое выполнение с заходом

в вызываемые функции:  клавиша F11 (кнопка Step или пункт меню Debug/Step)

Шаговое выполнение без захода

в вызываемые функции:  клавиша F10 (кнопка Step Over или пункт меню Debug/Step Over)

Непрерывное выполнение:  клавиша F5 (кнопка Run или пункт меню Debug/Run)

Возврат из режима отладки: клавиша Ctrl+F5 (кнопка или меню Debug/Start/Stop Debug Session)

 

   

ПОРЯДОК ВИЗУАЛИЗАЦИИ ГРАФИКА ФУНКЦИИ

 

Откомпилировать проект (F7).

Инициировать режим отладки (Ctrl+F5).

Поставить точку остановки на последний оператор - while(1) - должна присутствовать красная метка слева.

Открыть логический анализатор: меню View / Analysis Windows / Logic Analyzer или

кнопка Analysis Windows / Logic Analyzer.

Если левое поле анализатора пусто, в тексте программы последовательно выделить имена

Data1, Data2, Data3 и перетащить их на это поле анализатора.

Запустить программу на выполнение (F5).

После остановки отрегулировать масштаб диаграмм полем Zoom (In/Out/All).

Возвратиться из отладочного режима (Ctrl+F5).

*/

//---------------------------------------------------------------------------

// Заголовочные файлы

#include <stdint.h>                //Объявления типов данных

#include <math.h>                  //Функции стандартной библиотеки C/C++

#include "arm_math.h"              //Функции стандартной библиотеки CMSIS

#include "lcd.h"                   //Функции работы с дисплеем

#include "values.h"                //Числовые значения для операндов

#ifndef SIMUL

#include "stm32_p407.h"            //Файл конфигурации отладочной платы STM32-P407

#endif                             // (обходится при компиляции в режиме симуляции)

 

// Прототипы вызываемых функций

int16_t func(float arg);                                    //В этом файле

void LCD_DiagramDraw(short* Samples, int begindex, int length); //В файле diagr.c

 

// Глобальные параметры (доступны всем подпрограммам и другим модулям программы)

#define NBuf 1000               //Длина буфера данных (константа)

int16_t BufData[NBuf];             //Буфер данных (массив размером Nbuf 16-разрядных элементов)

int16_t Data1, Data2, Data3;       //Текущие значения функций

int8_t y1, y2;                    //Промежуточные значения

int16_t y3;

int dIndex, dLength, dMin;         //Параметры для построения диаграммы

 

//---------------------------------------------------------------------------

// ГЛАВНАЯ ФУНКЦИЯ

// Имеет стандартное имя: main.

// При запуске программы управление передается на первый оператор этой функции.

// Главная функция не должна иметь выхода.

int main()

{

//Объявление локальных переменных

int i;                     //Счетчик цикла

float ampl1;               //Амплитуда для функции 1

float ampl2;               //Амплитуда для функции 2

float arg1 = 0, arg2 = 0;  //Начальные значения аргументов функций

                            //Задание минимального и максимального значений аргумента

float arg1_min = 0, arg1_max = 90*PI,

   arg2_min = 0, arg2_max = 9*PI;

 

//Цикл вычисления функций

for (i = 0; i < NBuf; i++) //Счетчик (i) меняется от 0 до NBuf-1 с шагом 1

{

//:::::: Начало одного цикла вычислений :::::::::::::::::::::::::::::::::

ampl1 = 0.0 + 1.0 * i/NBuf; //Имитация изменения амплитуд сигналов

ampl2 = 0.4 + 0.8 * i/NBuf;

   

Data1 = ampl1 * func(arg1); //Функция 1 от аргумента 1

Data2 = ampl2 * func(arg2); //Функция 2 от аргумента 2

 

Data3 = Data1 + Data2;   //Функция 3 как сумма функций 1 и 2

                             //Вычисление аргумента по текущему номеру отсчета

arg1 = arg1_min + (arg1_max - arg1_min) * i / NBuf;

arg2 = arg2_min + (arg2_max - arg2_min) * i / NBuf;

//:::::: Конец одного цикла вычислений ::::::::::::::::::::::::::::::::::

    

BufData[i] = Data3;      //Сохранение значения функции 3 в буфере

}

 

//Следующий фрагмент, ограниченный директивами условной компиляции (#if.. #else),

// компилируется только для режима симуляции (Project: simulator)

#ifdef SIMUL                 //========================================================

 

while (1);                 //Остановка выполнения: оператор представляет собой

                             // бесконечный цикл, не позволяющий выйти из функции main

 

#else                        //========================================================

//Следующий фрагмент, ограниченный директивами условной компиляции (#else..#endif),

// компилируется только для отладочной платы (Project: board)

 

//Инициализация кнопок и джойстика

STM_PBInit(BUTTON_TAMPER, BUTTON_MODE_GPIO); //Кнопка TAMPER (здесь не используется)

STM_PBInit(BUTTON_WAKEUP, BUTTON_MODE_GPIO); //Кнопка WAKEUP (для завершения программы)

STM_PBInit(BUTTON_RIGHT, BUTTON_MODE_GPIO); //Позиции джойстика

STM_PBInit(BUTTON_LEFT, BUTTON_MODE_GPIO); // - " -

STM_PBInit(BUTTON_UP, BUTTON_MODE_GPIO); // - " -

STM_PBInit(BUTTON_DOWN, BUTTON_MODE_GPIO); // - " -

STM_PBInit(BUTTON_SEL, BUTTON_MODE_GPIO); // - " -

 

//Инициализация дисплея

LCD_Init();

 

//Значения по умолчанию для вывода диаграммы

  dIndex = 0;                              //Начальный номер отсчета

dMin = 15;                               //Минимальное число отсчетов

dLength = dMin*8;                        //Текущее число отсчетов

 

//Начальное построение диаграммы на дисплее

LCD_DiagramDraw(BufData, dIndex, dLength);

 

//Бесконечный цикл в основной программе

while (true)

{

//Проверка положения джойстика "Влево" и возможности смещения диаграммы к началу

if (STM_PBGetState(BUTTON_LEFT) && dIndex > 0)

{ if (dIndex - dLength > 0) dIndex -= dLength;

else dIndex = 0;

LCD_DiagramDraw(BufData, dIndex, dLength);

while (STM_PBGetState(BUTTON_LEFT)); //Ожидание отпускания кнопки

}

//Проверка положения джойстика "Вправо" и возможности смещения диаграммы к концу

if (STM_PBGetState(BUTTON_RIGHT) && dIndex + dLength < NBuf)

{ if (dIndex + dLength*2 < NBuf) dIndex += dLength;

else dIndex = NBuf - dLength;

LCD_DiagramDraw(BufData, dIndex, dLength);

while (STM_PBGetState(BUTTON_RIGHT));

}

//Проверка положения джойстика "Вверх" и возможности раздвижки диаграммы

if (STM_PBGetState(BUTTON_UP) && dLength > dMin)

{ dLength /= 2;

LCD_DiagramDraw(BufData, dIndex, dLength);

while (STM_PBGetState(BUTTON_UP));

}

//Проверка положения джойстика "Вниз" и возможности сдвижки диаграммы

if (STM_PBGetState(BUTTON_DOWN) && dLength < NBuf/2)

{ dLength *= 2;

if (dIndex + dLength > NBuf) dIndex = NBuf - dLength;

LCD_DiagramDraw(BufData, dIndex, dLength);

while (STM_PBGetState(BUTTON_DOWN));

}

 

//Проверка воздействия на кнопку WAKEUP, при нажатии - сброс, эквивалентный

// нажатию кнопки RESET (здесь - завершение данной программы)

if (STM_PBGetState(BUTTON_WAKEUP)) NVIC_SystemReset();

}

 

#endif //Конец компиляции операторов работы с отладочной платой

}

 

//---------------------------------------------------------------------------

// ФУНКЦИЯ

// Обратите внимание, что аргумент arg - вещественное число (с плавающей точкой),

// значение же функции приводится к целочисленному 16-разрядному значению

int16_t func(float arg)

return sinf(arg) * 32767;

}

//---------------------------------------------------------------------------

 

 

Исходный модуль ..\Dsp1_graph\diagr.c

//---------------------------------------------------------------------------

//

// УЧЕБНЫЙ ПРОЕКТ

// Модуль построения диаграмм

//

// Copyright (C) 2017 МИРЭА

//

//---------------------------------------------------------------------------

 

#include "lcd.h"                   //Функции для работы с дисплеем

 

#define MAX_SAMPLE 0x7FFF         //Максимальная амплитуда отсчетов

unsigned int ColorText = 0x0F0;    //Цвет символов текста

unsigned int ColorAxis = 0xF00;      //Цвет осей диаграммы

unsigned int ColorCurve = 0xFFF;   //Цвет основной кривой

                                   //Границы диаграммы в экранных пикселах:

int r_left = 10;                   // слева

int r_top = 30;                    // сверху

int r_right = 120;                 // справа

int r_bottom = 120;                // снизу

 

//---------------------------------------------------------------------------

// ПОДПРОГРАММА ПОСТРОЕНИЯ ГРАФИКА ФУНКЦИИ

// Входные параметры:

// Samples - указатель на массив значений функции

// begindex - индекс начальной точки

// length - число точек

// Для экранных координат начало (0,0) находится в левом верхнем углу

void LCD_DiagramDraw(short* Samples, int begindex, int length)

{

short sample;

int i, x, y;

int Y0 = r_top + (r_bottom - r_top) / 2;            //Y-координата горизонтальной оси

LCD_Clear(1);                                       //Очистка экрана

LCD_FontColor(ColorText); LCD_FontSize(9);          //Задание цвета и размера шрифта заголовка

LCD_print(" Диапазон отсчетов\r\n"                  //Вывод заголовка

       " %d...%d", begindex, begindex + length);

LCD_PenColor(ColorAxis);                            //Задание цвета осей

LCD_Rectangle(r_left-1, r_left-1, r_top-1, r_bottom+1); //Построение вертикальной оси

LCD_Rectangle(r_left-1, r_right+1, Y0, Y0);         //Построение горизонтальной оси

LCD_PenColor(ColorCurve);                           //Задание цвета точек основной кривой

 

Samples += begindex;                                //Начальный адрес значений функции

for (i = 0; i < length; i++)                        //Цикл по всем выводимым точкам

{

sample = *Samples++;                              //Чтение значения из массива с инкрементом указателя

x = r_left + (r_right - r_left) * i / length;     //X-координата точки

y = Y0 - (r_bottom - Y0) * sample / MAX_SAMPLE;   //Y-координата точки

LCD_Rectangle(x, x+1, y, y+1);                    //Построение точки

}

}

 

//---------------------------------------------------------------------------

 


Лабораторная работа № 2 (п.п. 2, 4)

Периферийные модули процессоров ARM Cortex-M4
и их использование при вводе, выводе, обработке сигналов

 

Выполнил студент:                                             Дата выполнения:

 

 

2. Генерация тональных сигналов с частотой, управляемой напряжением

 

 

Текст программы

 

Исходный модуль ..\Dsp2_ton\main.c

 

//---------------------------------------------------------------------------

//

// УЧЕБНЫЙ ПРОЕКТ

// Генерация тональных сигналов с логическими уровнями. Преобразование "напряжение-частота".

//

// Тональные сигналы формируются таймером, к выходу которого непосредственно подключен

// звуковой излучатель.

// Управляющее напряжение в диапазоне 0...3.3 В снимается с потенциометра и подается

// на вход АЦП (разряд порта PC0).

// Copyright (C) 2017 МИРЭА

//

//---------------------------------------------------------------------------

 

#include "stm32_p407.h"            //Файл конфигурации отладочной платы STM32-P407

#include "math.h"                  //Математические функции стандартной библиотеки C/C++

#include "adc.h"                   //Функции для работы с АЦП

#include "lcd.h"                   //Функции для работы с дисплеем

 

/*

Справка: Частоты музыкальных тонов

 

Индекс Нота Частота | Индекс Нота Частота | Индекс Нота Частота | Индекс Нота Частота

             Гц |             Гц |             Гц  |             Гц

                     |                     |                     |

0 до 261.63 | 12 до 523.25 | 24 до 1046.50 | 36 до 2093.01

1      277.18 | 13      554.37 | 25     1108.73 | 37     2217.46

2 ре 293.66 | 14 ре 587.33 | 26 ре 1174.66 | 38 ре 2349.32

3      311.13 | 15      622.25 | 27     1244.51 | 39      2489.02

4 ми 329.63 | 16 ми 659.26 | 28 ми 1318.51 | 40 ми 2637.02

5 фа 349.23 | 17 фа 698.46 | 29 фа 1396.91 | 41 фа 2793.83

6      369.99 | 18      739.99 | 30     1479.98 | 42     2960.00

7 соль 392.00 | 19 соль 783.99 | 31 соль 1567.98 | 43 соль 3135.96

8      415.30 | 20      830.61 | 32     1661.22  | 44     3322.44

9 ля 440 | 21 ля 880.00 | 33 ля 1760.00 | 45 ля 3520

10      466.16 | 22      932.33 | 34     1864.66 | 46     3729.31

11 си 493.88 | 23 си 987.77 | 35 си 1975.54 | 47 си 3951.07

                                                                           | 48 до 4186.02

*/

 

//Таблица частот музыкальных тонов (одна строка составляет октаву)

float ToneTable[] =

{ 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440, 466.16, 493.88,

523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61, 880, 932.33, 987.77,

 1046.50, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760, 1864.66, 1975.54,

 2093.01, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2960.00, 3135.96, 3322.44, 3520, 3729.31, 3951.07,

 4186.02 };

 

#define Fmin 20          //Значения минимальной и максимальной частоты

#define Fmax 20000       // преобразователя "напряжение-частота"

 

//Прототипы вызываемых функций

void TimerConfig(void);

 

//---------------------------------------------------------------------------

// ГЛАВНАЯ ФУНКЦИЯ

int main()

{

uint32_t ToneIndex;                      //Индекс музыкального тона

float F;                                 //Текущая генерируемая частота, Гц

uint16_t AdcData;                        //Код с АЦП

int mode = 0;                            //Тип генерируемого сигнала

 

//Инициализация органов управления

STM_PBInit(BUTTON_TAMPER, BUTTON_MODE_GPIO); //Кнопка TAMPER

STM_PBInit(BUTTON_WAKEUP, BUTTON_MODE_GPIO); //Кнопка WAKEUP

STM_PBInit(BUTTON_RIGHT, BUTTON_MODE_GPIO); //Позиции джойстика

STM_PBInit(BUTTON_LEFT, BUTTON_MODE_GPIO);

STM_PBInit(BUTTON_UP, BUTTON_MODE_GPIO);

STM_PBInit(BUTTON_DOWN, BUTTON_MODE_GPIO);

STM_PBInit(BUTTON_SEL, BUTTON_MODE_GPIO);

 

//Инициализация индикаторов

  STM_LEDInit(LED4); STM_LEDOff(LED4);

 

//Инициализация дисплея

LCD_Init(); LCD_Clear(1);

LCD_FontSize(11);

LCD_TextPos(2,1); LCD_print("КОД АЦП");

LCD_TextPos(2,4); LCD_print("Частота, Гц");

 

//Инициализация и первичный запуск АЦП (см. файл adc.c)

ADC_Initialize(ADC2, ADC_Channel_10, 0);

ADC_SoftwareStartConv(ADC2);

 

//Конфигурирование таймера (см. файл tim_ton.c)

TimerConfig();

 

//Цикл в основной программе

while (1)

{

//Проверка положений джойстика

if (STM_PBGetState(BUTTON_DOWN)) mode = 1;

else

if (STM_PBGetState(BUTTON_UP)) mode = 2;

else mode = 0;

 

//Получение кода с АЦП

while (!ADC_GetFlagStatus(ADC2, ADC_FLAG_EOC)); //Ожидание окончания преобразования

AdcData = ADC_GetConversionValue(ADC2);      //Чтение кода с АЦП (выравнен влево)

ADC_SoftwareStartConv(ADC2);                 //Новый запуск преобразования

Delay_ms(20);                                //Временная задержка

 

//Генерация сигнала

switch (mode)

{ //Нет генерации

case 0:

   TIM_Cmd(TIM3, DISABLE);

   break;

//Музыкальные тона, заданные таблично (индекс пропорционален коду АЦП)

case 1:

   ToneIndex = (AdcData & 0xFF00) * sizeof(ToneTable) / sizeof(float) / 65536;

     F = ToneTable[ToneIndex];

   TIM_SetAutoreload(TIM3, 1e6f/F - 1);

   TIM_Cmd(TIM3, ENABLE);

   break;

//Частоты в заданном диапазоне (используется степенная зависимость от напряжения)     

case 2:

   F = Fmin + (Fmax-Fmin) * powf((AdcData & 0xFF00) / 65536.0, 3);

   TIM_SetAutoreload(TIM3, 1e6f/F - 1);

   TIM_Cmd(TIM3, ENABLE);

   break;

}

 

//Справочный вывод на дисплей кода АЦП в 16- и 10-й системах, частоты сигнала

LCD_TextPos(2,2); LCD_print("0x%04X %5d ", AdcData, AdcData);

LCD_TextPos(2,5); LCD_print("%.1f ", mode ? F : 0.0f);

 

//Проверка нажатия кнопки WAKEUP завершения работы (сброса процессора)

if (STM_PBGetState(BUTTON_WAKEUP)) NVIC_SystemReset();

}

 

}

//---------------------------------------------------------------------------

 

 

Исходный модуль ..\Dsp2_ton\tim_ton.c

//---------------------------------------------------------------------------

//

// УЧЕБНЫЙ ПРОЕКТ

// Работа с таймерами. Режим деления частоты

// Copyright (C) 2016 МИРЭА

//

//---------------------------------------------------------------------------

 

#include "stm32f4xx.h"             //Константы и структуры данных для процессоров семейства STM32F4xx

 

//---------------------------------------------------------------------------

// ИНИЦИАЛИЗАЦИЯ ТАЙМЕРА

/* Таймер 3 подключен к шине APB1, тактируется частотой 84 МГц.

Предделитель с коэффициентом деления 42 снижает частоту до 2 МГц.

Основной коэффициент деления определяется частотой музыкального тона,

при этом следует учесть, выходная частота получается еще в 2 раза меньше,

так как при каждом переполнении счетного регистра формируется полпериода сигнала.

Выход таймера - канал CH4 - выведен на разряд порта PB1.

*/

void TimerConfig(void)

{

 

GPIO_InitTypeDef GPIO_InitStructure;              //Структура конфигурации портов общего назначения

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;   //Структура конфигурации базового таймера

TIM_OCInitTypeDef TIM_OCInitStructure;              //Структура конфигурации таймера с ШИМ

 

//Разрешение тактирования таймера

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

 

//Разрешение тактирования порта PB

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

 

//Конфигурирование разряда порта PB1 как выходного с альтернативной функцией

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;         //Номер разряда (маска)

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;      //Альтернативная функция

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //Задание быстродействия

GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;    //Установка типа выходного каскада: двухтактный

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;      //Подтягивающий резистор: к "+" питания

GPIO_Init(GPIOB, &GPIO_InitStructure);            //Функция конфигурирования

 

//Подключение выхода канала CH4 таймера TIM3 к разряду порта PB1

GPIO_PinAFConfig(GPIOB, GPIO_PinSource1, GPIO_AF_TIM3);

 

//Базовая конфигурация таймера

TIM_TimeBaseStructure.TIM_Period = 1000-1;               //Основной коэф.деления по умолчанию

TIM_TimeBaseStructure.TIM_Prescaler = 42-1;              //Предделение до 2 МГц

TIM_TimeBaseStructure.TIM_ClockDivision = 0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //Счетчик вверх: от 0 до TIM_Period

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);          //Функция конфигурирования

 

//Конфигурирование ШИМ

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;      //Режим выхода: переключение уровня

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //Разрешение аппаратного выхода

TIM_OCInitStructure.TIM_Pulse = 0;                       //Пороговое значение для переключения уровня

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //Полярность импульса – положительная

TIM_OC4Init(TIM3, &TIM_OCInitStructure);                 //Функция конфигурирования

 

//Работу таймера пока не разрешаем

TIM_Cmd(TIM3, DISABLE);

}

 

//---------------------------------------------------------------------------

 


4. Генерация сигнала методом прямого цифрового синтеза.
Быстрое преобразование Фурье (БПФ)

 

4.1. Проверка работы генератора гармонического сигнала и аналого-цифрового преобразования.

Частота дискретизации канала АЦП-ЦАП Fs = …   Гц

 

Частота

Вход АЦП (выход генератора)

 

Выход ЦАП                                                       Спектральная характеристика

 

Частота

Вход АЦП (выход генератора)

Выход ЦАП                                                       Спектральная характеристика

 

Частота

Вход АЦП (выход генератора)

Выход ЦАП                                                       Спектральная характеристика

 


4.2. Исследование особенностей БПФ.

Полный диапазон частот: ...

"Вырезанный" диапазон: ...

 

Форма выходного сигнала в нескорректированном диапазоне частот.

Частота ... Гц

 

Форма выходного сигнала внутри скорректированного диапазона частот.

Частота ... Гц

 

Форма выходного сигнала на границе диапазонов.

Частота ... Гц

 

 

Причины "артефактов":

 

4.3. Использование оконной функции.

Частота сигнала:                                     Частота сигнала:

Спектрограмма без оконной функции          Спектрограмма без оконной функции

 

Спектрограмма с оконной функцией           Спектрограмма с оконной функцией

 


4.4. Измерение времени обработки.

 

Вид обработки Время ввода-вывода, мкс Время обработки блока, мкс Время ввода-вывода и обработки отсчета, мкс 
FFT + IFFT       рассчитать
FFT + IFFT + частотная коррекция     рассчитать
FFT + IFFT + оконная функция     рассчитать

 

 

Расчет максимального коэффициента загрузки процессора:

 

 


Текст программы

 

Исходный модуль ..\DspLab_fft\main.c

/******************************************************************************

 *

 * УЧЕБНЫЙ ПРОЕКТ

 *

 * Генерация сигнала методом прямого цифрового синтеза.

 * Прямое, обратное быстрое преобразование Фурье (БПФ).

 * Визуализация спектра сигнала. Оконная функция.

 *

 * Задействовано два переключаемых буфера отсчетов.

 * Демонстрируется возникновение переходных помех на стыке временных отсчетов

 * после прямого БПФ, коррекции частотных составляющих и обратного БПФ.

 *

 * Для обработки используется библиотека CMSIS DSP Library.

 *

 * Copyright (C) 2015 МИРЭА

 *

 *  Версия: 1.3

 *

 ******************************************************************************/

/*

ОРГАНЫ УПРАВЛЕНИЯ

Джойстик "вверх/вниз" - увеличение/уменьшение частоты генератора синусоидального сигнала

Джойстик "влево" - переключение между типами входного сигнала: синусоидальный / шум / нет сигнала

Джойстик "вправо" - переключение режимов обработки: без обработки / коррекция отсчетов / оконная функция

Джойстик "выбор" - переключение режимов регистрации: измерение коэф.передачи / нет измерений / спектр

Кнопка TAMPER    - вывод меток времени обработки

Кнопка WAKEUP    - выход из программы

*/

 

// Заголовочные файлы

#include "stm32_p407.h"            //Файл конфигурации отладочной платы STM32-P407

#include "adcdac.h"                //Процедуры работы с АЦП, ЦАП, кодеком

#include "gauge.h"                 //Процедуры управления и измерения

#include "arm_math.h"              //Определения и функции библиотеки CMSIS DSP Library

#include "mathfunc.h"              //Прототипы вспомогательных математических функций

 

//---------------------------------------------------------------------------

// ОБЪЯВЛЕНИЯ ДАННЫХ

 

#define fftLen 1024            //Длина - число отсчетов - преобразования Фурье

#define BlockSize (fftLen*2)      //Размер буфера для отсчетов с учетом мнимых составляющих

 

float32_t DataIn0[BlockSize];      //Буферы для входных отсчетов

float32_t DataIn1[BlockSize];

float32_t DataOut0[BlockSize];     //Буферы для выходных отсчетов

float32_t DataOut1[BlockSize];

float32_t *DataIn, *DataOut;       //Текущие указатели на буферы

arm_cfft_radix4_instance_f32 S;    //Структуры для обработки данных

arm_cfft_radix4_instance_f32 iS;

 

int32_t DataIndex;                 //Индекс текущего входного/выходного отсчета

int32_t TransferBuf;               //Номер (0 или 1) буфера для текущего ввода-вывода

int32_t ProcBuf;                   //Номер (0 или 1) обрабатываемого (обработанного) буфера

int16_t DataChannel1, DataChannel2; //Входные и выходные отсчеты в формате 1.15 с фиксированной точкой

int16_t DataADC;                   //Прямой отсчет с АЦП для системы калибровки

int32_t AmplOffset = 0;            //Смещение нулевого уровня сигнала с АЦП

int32_t GagingMode = 0;            //Режим регистрации:

                                   // 0 - измерение коэффициента передачи

                                   // 1 - нет измерений

                                   // 2 - визуализация спектра

int32_t ProcessMode = 0;           //Дополнительный режим обработки (помимо БПФ):

                                   // 0 - нет

                                   // 1 - коррекция частотных составляющих

                                   // 2 - оконная функция

int32_t HardwareMode = 0;          //Конфигурация системы:

                                   // 0: цифр. генератор -> ЦАП--(аналоговый сигнал)--> АЦП -> обработка

                                   // 1: цифр. генератор -> обработка

 

//---------------------------------------------------------------------------

// ГЛАВНАЯ ФУНКЦИЯ

int main()

{

int32_t j;

float K;

 

//Счетчик для реализации периода изменения тестовой индикации

volatile uint32_t i = 0;

 

//Инициализация структур параметров для цифровой обработки сигнала

arm_cfft_radix4_init_f32 (&S, fftLen, 0, 1); //Прямое преобразование

arm_cfft_radix4_init_f32(&iS, fftLen, 1, 1); //Обратное преобразование

 

//Задание 4-х уровней групповых приоритетов и 4-х уровней приоритетов в каждой группе

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

 

//Инициализация тестовых индикаторов

STM_LEDInit(LED1);

STM_LEDInit(LED2);

 

//Инициализация кнопок

STM_PBInit(BUTTON_TAMPER, BUTTON_MODE_GPIO);

STM_PBInit(BUTTON_WAKEUP, BUTTON_MODE_GPIO);

 

//Инициализация кодека (ЦАП выходных отсчетов сигнала)

SoundCodecConfig(32000);                 //Аргумент - частота дискретизации, Гц

SoundCodecInterruptConfig(ENABLE);

 

//Инициализация АЦП

ADCConfig();

 

//Инициализация генератора входного сигнала

SignalGenConfig();

 

//Инициализация графического интерфейса, джойстика, системы измерений

GgInterfaceInit();

 

//ОСНОВНОЙ ЦИКЛ

while (1)

{

if (TransferBuf != ProcBuf)             //Ожидание заполнения буфера

{

TimeMarker3();                        //Метка времени выполнения № 3 (НАЧАЛО ОБРАБОТКИ)

DataIn = ProcBuf ? DataIn1 : DataIn0; //Установка указателей на обрабатываемые буферы

DataOut = ProcBuf ? DataOut1 : DataOut0;

 

// ===== Н а ч а л о о б р а б о т к и =====

STM_LEDOn(LED2);

if (ProcessMode == 2)

   for (j = 0; j < fftLen/2; j++)      //Взвешивание посредством оконной функции Хемминга

      { K = 0.54f - 0.46f * cosf(2*3.14159f*j/(fftLen-1));

     DataIn[j*2] *= K;

     DataIn[(fftLen-1-j)*2] *= K;

   }

     

arm_cfft_radix4_f32(&S, DataIn);      //ПРЯМОЕ БПФ

for (j = 0; j < BlockSize; j++)       //Пересылка частотных отсчетов (без обработки)

   DataOut[j] = DataIn[j];

   

if (ProcessMode == 1)

{                                     //Тестовая обработка: уменьшение в K раз амплитуд отсчетов

   K = 0.1f;                           // с индексами в заданном диапазоне и симметричных им.

   for (j = 64; j <= 128; j++)         // Для дискретизации 32 кГц и 1024 отсчетах индексы 64..128

   {                                   // примерно соответствуют частотам сигнала 2..4 кГц.

     DataOut[j*2] *= K;                //Действительный частотный отсчет

     DataOut[j*2+1] *= K;              //Мнимый частотный отсчет

     DataOut[(fftLen-j)*2] *= K;       //Действительный частотный отсчет в отрицательной области

     DataOut[(fftLen-j)*2+1] *= K;     //Мнимый частотный отсчет в отрицательной области

   }

}

     

SpectrVisualization(DataOut, fftLen); //Визуализация спектра

arm_cfft_radix4_f32(&iS, DataOut);    //ОБРАТНОЕ БПФ

STM_LEDOff(LED2);

// ===== К о н е ц о б р а б о т к и =====

 

ProcBuf ^= 1;                         //Переключение на другой буфер

TimeMarker4();                        //Метка времени выполнения № 4 (КОНЕЦ ОБРАБОТКИ)

}

 

GgControl();                            //Управление частотой и амплитудой генератора сигнала,

                                            // вывод результатов измерений

if (i++ == 0x800) STM_LEDOff(LED1);     //Тестовое управление индикатором

if (i == 0x50000) STM_LEDOn(LED1), i = 0;

 

if (STM_PBGetState(BUTTON_WAKEUP)) break; //Выход из основного цикла, если нажата WAKEUP

}

//ОКОНЧАНИЕ ОСНОВНОГО ЦИКЛА

 

//Сброс процессора - завершение выполнения данной программы, запуск начального загрузчика

NVIC_SystemReset();

 

while (1) {}

}

 

//---------------------------------------------------------------------------

// ОБСЛУЖИВАНИЕ ПРЕРЫВАНИЯ С ЧАСТОТОЙ ДИСКРЕТИЗАЦИИ

// Данная подпрограмма вызывается из обработчика прерывания ..._IRQHandler(),

// реализованного в adcdac.c

// Частота дискретизации формируется интерфейсным модулем процессора SPI/I2S,

// посредством которого по интерфейсу I2S отсчеты сигнала выводятся на внешний звуковой кодек.

// Этот модуль также генерирует прерывания с частотой дискретизации. В обработчике

// прерывания, размещенном здесь, производятся следующие операции:

// - считываются отсчеты входного сигнала с АЦП или цифрового входа;

// - АЦП запускается на следующий цикл преобразования;

// - входные отсчеты размещаются в одном из входных буферов;

// - из выходного буфера с тем же номером извлекаются обработанные отсчеты и передаются кодеку;

// - проверяется заполнение текущего буфера и при необходимости производится переключение на другой.

 

void Sample_Handler(void)

{

extern int16_t GeneratorSamples; //Цифровой выход генератора сигналов (в доп. коде)

                                  // параметр объявлен в adcdac.c

if (HardwareMode == 0)

{ //Получение данных с АЦП, преобразование смещенного кода в дополнительный,

// коррекция нулевого уровня

DataADC = ADC_GetConversionValue(ADC1) ^ 0x8000;

DataChannel2 = DataChannel1 = DataADC - AmplOffset;

}

//Получение цифрового отсчета входного сигнала (минуя АЦП)

else DataChannel2 = DataChannel1 = GeneratorSamples;

 

//Перезапуск АЦП

ADC_SoftwareStartConv(ADC1);

 

//Сохранение входных отсчетов в текущем буфере, чтение выходных отсчетов

if (TransferBuf)

{ DataIn1[DataIndex] = q15_to_float(DataChannel1); DataIn1[DataIndex+1] = 0;

DataChannel2 = DataChannel1 = float_to_q15(DataOut1[DataIndex]); DataIndex += 2;

}

else 

{ DataIn0[DataIndex] = q15_to_float(DataChannel1); DataIn0[DataIndex+1] = 0;

DataChannel2 = DataChannel1 = float_to_q15(DataOut0[DataIndex]); DataIndex += 2;

}

//Проверка достижения конца буфера, если да, переключение на другой буфер

if (DataIndex >= BlockSize) DataIndex = 0, TransferBuf ^= 1;

 

//Обработанные значения - в DataChannel1, DataChannel2

}

 

 

Фрагменты исходного модуля ..\DspLab_fft\adcdac.c

 

//---------------------------------------------------------------------------

// ИЗМЕНЕНИЕ ЧАСТОТЫ ГЕНЕРАТОРА

// Входной параметр: dir = -1 (вниз), 0 - нет, 1 (вверх)

void GenFrequencyChange(int32_t dir)

{

uint32_t dfreq;

if (dir == 0) return;

dfreq = (dir < 0) ? GenFrequency - 1 : GenFrequency;

if (dfreq >= 5000) dfreq = 50;

else if (dfreq >= 2000) dfreq = 20;

  else if (dfreq >= 1000) dfreq = 10;

       else if (dfreq >= 500) dfreq = 5;

            else if (dfreq >= 200) dfreq = 2;

                 else dfreq = 1;

if (dir > 0)

if (GenFrequency + dfreq > GenFrequencyMax) GenFrequency = GenFrequencyMax;

else GenFrequency += dfreq;

else

if (GenFrequency < GenFrequencyMin + dfreq) GenFrequency = GenFrequencyMin;

else GenFrequency -= dfreq;

DeltaPhase = UINT32_MAX / GenReference * GenFrequency;

}

 

 

//---------------------------------------------------------------------------

// ОБРАБОТЧИК ПРЕРЫВАНИЯ ОТ ТАЙМЕРА 4 - ИСТОЧНИКА ОПОРНОЙ ЧАСТОТЫ DDS

// Вызывается с частотой дискретизации генерируемого сигнала.

// Проверяется флаг типа прерывания (в данном случае это необязательно,

// так как используется один тип), флаг очищается.

// Код для передачи в ЦАП - смещенный.

void TIM4_IRQHandler(void)

{

if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)

{

TIM_ClearITPendingBit(TIM4, TIM_IT_Update);

AccumulatorPhase += DeltaPhase;

if (!GenAmplitude) GeneratorSamples = 0;

else

if (GenNoise)

   GeneratorSamples = (int16_t)rand() / 2;

else

   GeneratorSamples = (sinTableQ15[AccumulatorPhase >> 24] / 2) * GenAmplitude;

DACData1 = GeneratorSamples ^ 0x8000;

DAC_SetChannel1Data(DAC_Align_12b_L, DACData1);

DACData1 += 32;

}

}

 

//---------------------------------------------------------------------------

 

Фрагменты исходного модуля ..\DspLab_fft\gauge.c

 

//---------------------------------------------------------------------------

// ОБРАБОТЧИК ПРЕРЫВАНИЙ ОТ ТАЙМЕРА ПЕРИОДА ОПРОСА РЕГУЛЯТОРА ЧАСТОТЫ

uint32_t ButCntr = 0;                   //Код длительности удержания органа управления

void TIM6_DAC_IRQHandler(void)

{

TIM_ClearITPendingBit(TIM6, TIM_IT_Update);

if (STM_EVAL_PBGetState(BUTTON_UP))   //Положение джойстика "Вверх" -

{                                     // увеличение частоты

if (ButCntr == 0 || ButCntr > 4)

GenFrequencyChange(1);

ButCntr++;

}

else

if (STM_PBGetState(BUTTON_DOWN))    //Положение джойстика "Вниз" -

{                                   // уменьшение частоты

if (ButCntr == 0 || ButCntr > 4)

   GenFrequencyChange(-1);

ButCntr++;

}

else

if (ButCntr) ButCntr = 0, TIM_SetAutoreload(TIM6, RegulDelay1);

 

//Увеличение скорости перестройки при длительном удержании

if (ButCntr > 30) TIM_SetAutoreload(TIM6, RegulDelay2);

}

 

//---------------------------------------------------------------------------

 

 


Дата добавления: 2018-04-05; просмотров: 701; Мы поможем в написании вашей работы!

Поделиться с друзьями:




Мы поможем в написании ваших работ!