Конечно. Ниже приведены полные листинги файлов `main.c` и `init.c` для Лабораторных работ 1, 2, 3, 4 и 6. Код написан с использованием библиотеки **Standard Peripheral Library (SPL)**, как это требуется в методическом пособии. --- ### Лабораторная работа 1: GPIO (Мигание светодиодами) **Задача:** Поочередное циклическое включение светодиодов (Зеленый -> Оранжевый -> Красный -> Синий). #### `init.c` ```c #include "init.h" void LEDs_ini(void) { GPIO_InitTypeDef GPIO_Init_LED; // 1. Включаем тактирование порта D (где сидят светодиоды) RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); // 2. Настраиваем пины 12, 13, 14, 15 GPIO_Init_LED.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_Init_LED.GPIO_Mode = GPIO_Mode_OUT; // Режим: Выход GPIO_Init_LED.GPIO_Speed = GPIO_Speed_2MHz; // Скорость: Низкая (экономия энергии) GPIO_Init_LED.GPIO_OType = GPIO_OType_PP; // Тип: Push-Pull (тяни-толкай) GPIO_Init_LED.GPIO_PuPd = GPIO_PuPd_NOPULL; // Без подтяжки // 3. Применяем настройки GPIO_Init(GPIOD, &GPIO_Init_LED); } ``` #### `main.c` ```c #include "main.h" // Простая функция задержки на цикле void Delay(volatile uint32_t nCount) { while(nCount--) {} } int main(void) { LEDs_ini(); // Вызов инициализации while(1) { // Зеленый (PD12) GPIO_SetBits(GPIOD, GPIO_Pin_12); Delay(2000000); GPIO_ResetBits(GPIOD, GPIO_Pin_12); // Оранжевый (PD13) GPIO_SetBits(GPIOD, GPIO_Pin_13); Delay(2000000); GPIO_ResetBits(GPIOD, GPIO_Pin_13); // Красный (PD14) GPIO_SetBits(GPIOD, GPIO_Pin_14); Delay(2000000); GPIO_ResetBits(GPIOD, GPIO_Pin_14); // Синий (PD15) GPIO_SetBits(GPIOD, GPIO_Pin_15); Delay(2000000); GPIO_ResetBits(GPIOD, GPIO_Pin_15); } } ``` --- ### Лабораторная работа 2: Прерывания и таймеры **Задача:** Переключение режимов мигания (Красный 8с / Синий 3с) по нажатию кнопки через прерывание. #### `init.c` ```c #include "init.h" // Настройка светодиодов void LEDs_ini(void) { GPIO_InitTypeDef GPIO_Init_LED; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); GPIO_Init_LED.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_Init_LED.GPIO_Mode = GPIO_Mode_OUT; GPIO_Init_LED.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init_LED.GPIO_OType = GPIO_OType_PP; GPIO_Init_LED.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOD, &GPIO_Init_LED); } // Настройка кнопки (PA0) void Button_ini(void) { GPIO_InitTypeDef GPIO_Init_Button; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); GPIO_Init_Button.GPIO_Pin = GPIO_Pin_0; GPIO_Init_Button.GPIO_Mode = GPIO_Mode_IN; // Режим: Вход GPIO_Init_Button.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init_Button.GPIO_OType = GPIO_OType_PP; GPIO_Init_Button.GPIO_PuPd = GPIO_PuPd_NOPULL; // PA0 уже имеет внешнюю подтяжку к земле на плате GPIO_Init(GPIOA, &GPIO_Init_Button); } // Настройка внешних прерываний (EXTI) для кнопки void EXTI_Config(void) { EXTI_InitTypeDef EXTI_InitStruct; NVIC_InitTypeDef NVIC_InitStruct; // Включаем тактирование SYSCFG RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); // Подключаем EXTI Line0 к порту PA0 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0); // Настраиваем линию EXTI0 EXTI_InitStruct.EXTI_Line = EXTI_Line0; EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising; // Срабатывание по переднему фронту (нажатие) EXTI_InitStruct.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStruct); // Настраиваем прерывание в NVIC (контроллере прерываний) NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x00; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); } ``` #### `main.c` ```c #include "main.h" volatile uint32_t ms_ticks = 0; volatile uint8_t mode = 0; // 0 - режим красного, 1 - режим синего // Обработчик системного таймера (вызывается каждую 1 мс) void SysTick_Handler(void) { ms_ticks++; } // Обработчик прерывания от кнопки void EXTI0_IRQHandler(void) { // Проверяем флаг прерывания if(EXTI_GetITStatus(EXTI_Line0) != RESET) { // Программный антидребезг (задержка) for(int i=0; i<100000; i++); // Если кнопка все еще нажата if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_SET) { mode = !mode; // Инвертируем режим // Гасим оба диода, чтобы начать цикл заново чисто GPIO_ResetBits(GPIOD, GPIO_Pin_14 | GPIO_Pin_15); ms_ticks = 0; // Сброс таймера цикла } // Очищаем флаг прерывания EXTI_ClearITPendingBit(EXTI_Line0); } } int main(void) { LEDs_ini(); Button_ini(); EXTI_Config(); // Настройка SysTick на 1 мс (если частота ядра 168МГц или другая - функция сама посчитает) SysTick_Config(SystemCoreClock / 1000); while(1) { if (mode == 0) { // Режим Красный: Период 8000 мс (4с горит, 4с не горит) if ((ms_ticks % 8000) < 4000) GPIO_SetBits(GPIOD, GPIO_Pin_14); else GPIO_ResetBits(GPIOD, GPIO_Pin_14); } else { // Режим Синий: Период 3000 мс (1.5с горит, 1.5с не горит) if ((ms_ticks % 3000) < 1500) GPIO_SetBits(GPIOD, GPIO_Pin_15); else GPIO_ResetBits(GPIOD, GPIO_Pin_15); } } } ``` --- ### Лабораторная работа 3: UART (Связь с ПК) **Задача:** Отправка сообщений в консоль и управление миганием по кнопке. #### `init.c` ```c #include "init.h" // Светодиоды и Кнопка настраиваются так же, как и раньше void LEDs_ini(void) { GPIO_InitTypeDef GPIO_Init_LED; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); GPIO_Init_LED.GPIO_Pin = GPIO_Pin_12; // Используем зеленый GPIO_Init_LED.GPIO_Mode = GPIO_Mode_OUT; GPIO_Init_LED.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init_LED.GPIO_OType = GPIO_OType_PP; GPIO_Init_LED.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOD, &GPIO_Init_LED); } void Button_ini(void) { GPIO_InitTypeDef GPIO_Init_Button; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); GPIO_Init_Button.GPIO_Pin = GPIO_Pin_0; GPIO_Init_Button.GPIO_Mode = GPIO_Mode_IN; GPIO_Init_Button.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_Init_Button); } // Настройка USART2 (PA2 - TX, PA3 - RX) void USART2_ini(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; // 1. Включаем тактирование GPIOA и USART2 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); // 2. Настраиваем пины PA2 и PA3 на альтернативную функцию GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStructure); // 3. Соединяем пины с альтернативной функцией AF7 (USART2) GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); // 4. Настраиваем параметры USART USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART2, &USART_InitStructure); USART_Cmd(USART2, ENABLE); // Включаем USART } ``` #### `main.c` ```c #include "main.h" #include volatile uint8_t is_blinking = 0; volatile uint32_t timer_ms = 0; // Переопределение fputc для работы printf через UART struct __FILE { int handle; }; FILE __stdout; int fputc(int ch, FILE *f) { USART_SendData(USART2, (uint8_t)ch); while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET){} return ch; } // Таймер для отсчета времени void SysTick_Handler(void) { if (is_blinking) { timer_ms++; if (timer_ms >= 200) { // Каждые 200 мс переключаем диод GPIO_ToggleBits(GPIOD, GPIO_Pin_12); timer_ms = 0; } } else { GPIO_ResetBits(GPIOD, GPIO_Pin_12); } } int main(void) { LEDs_ini(); Button_ini(); USART2_ini(); SysTick_Config(SystemCoreClock / 1000); uint8_t btn_prev = 0; while(1) { // Опрос кнопки uint8_t btn_curr = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0); if (btn_curr == 1 && btn_prev == 0) { // Нажатие (передний фронт) is_blinking = !is_blinking; // Смена состояния if (is_blinking) { printf("LED Status: BLINKING\r\n"); } else { printf("LED Status: STOPPED\r\n"); } // Простая задержка антидребезга for(int i=0; i<500000; i++); } btn_prev = btn_curr; } } ``` --- ### Лабораторная работа 4: АЦП (Измерение напряжения) **Задача:** Настройка АЦП и улучшение точности методом усреднения. #### `init.c` ```c #include "init.h" // Настройка USART2 (для вывода данных) - аналогична ЛР 3 void USART2_ini(void) { // ... (код идентичен init.c из ЛР 3) ... // Для экономии места, скопируйте функцию USART2_ini из ЛР 3 } // Настройка АЦП (ADC1 на канале 4 - пин PA4) void ADC_ini(void) { ADC_InitTypeDef ADC_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStructure; GPIO_InitTypeDef GPIO_InitStructure; // 1. Включаем тактирование ADC1 и GPIOA RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // 2. Настраиваем PA4 как аналоговый вход GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; // Аналоговый режим GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); // 3. Общие настройки АЦП ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; // Частота АЦП не должна превышать 36МГц ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; ADC_CommonInit(&ADC_CommonInitStructure); // 4. Настройки ADC1 ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ScanConvMode = DISABLE; // Один канал ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // Запуск вручную ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfConversion = 1; ADC_Init(ADC1, &ADC_InitStructure); // 5. Включаем ADC1 ADC_Cmd(ADC1, ENABLE); } ``` #### `main.c` ```c #include "main.h" #include #define N_SAMPLES 16 // Число выборок для усреднения // Функция для printf (такая же, как в ЛР 3) struct __FILE { int handle; }; FILE __stdout; int fputc(int ch, FILE *f) { USART_SendData(USART2, (uint8_t)ch); while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET){} return ch; } void Delay(volatile uint32_t nCount) { while(nCount--) {} } // Функция чтения и усреднения uint16_t Get_ADC_Average(void) { uint32_t sum = 0; // Настраиваем канал 4, время выборки 84 цикла ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_84Cycles); for (int i = 0; i < N_SAMPLES; i++) { ADC_SoftwareStartConv(ADC1); // Запуск преобразования while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); // Ждем окончания sum += ADC_GetConversionValue(ADC1); // Читаем результат } return (uint16_t)(sum / N_SAMPLES); } int main(void) { USART2_ini(); ADC_ini(); float voltage; uint16_t adc_val; while(1) { adc_val = Get_ADC_Average(); // Vref = 3.0V (на Discovery), 12 бит = 4095 voltage = (float)adc_val * 3.0f / 4095.0f; printf("ADC: %d, Voltage: %.3f V\r\n", adc_val, voltage); Delay(5000000); // Пауза } } ``` --- ### Лабораторная работа 6: ЦАП + DMA (Генерация сигнала) **Задача:** Генерация сложного сигнала (две гармоники) через DAC с использованием DMA и Таймера. #### `init.c` ```c #include "init.h" // Настройка GPIO для ЦАП (PA4) void GPIO_Ini(void) { GPIO_InitTypeDef gpio; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); gpio.GPIO_Pin = GPIO_Pin_4; gpio.GPIO_Mode = GPIO_Mode_AN; gpio.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &gpio); } // Настройка Таймера 2 (Триггер для ЦАП) void TIM_Ini(void) { TIM_TimeBaseInitTypeDef tim; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); tim.TIM_Prescaler = 0; // Максимальная частота tim.TIM_CounterMode = TIM_CounterMode_Up; tim.TIM_Period = 1000 - 1; // Период срабатывания (частота дискретизации) tim.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2, &tim); // Таймер генерирует событие TRGO при обновлении TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); TIM_Cmd(TIM2, ENABLE); } // Настройка ЦАП void DAC_Ini(void) { DAC_InitTypeDef dac; RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); dac.DAC_Trigger = DAC_Trigger_T2_TRGO; // Запуск от таймера 2 dac.DAC_WaveGeneration = DAC_WaveGeneration_None; dac.DAC_OutputBuffer = DAC_OutputBuffer_Enable; DAC_Init(DAC_Channel_1, &dac); DAC_Cmd(DAC_Channel_1, ENABLE); // Включаем запрос DMA от ЦАП DAC_DMACmd(DAC_Channel_1, ENABLE); } // Настройка DMA (Память -> ЦАП) // buffer - указатель на массив, size - размер массива void DMA_Ini(uint16_t *buffer, uint32_t size) { DMA_InitTypeDef dma; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); DMA_DeInit(DMA1_Stream5); // DAC1 висит на DMA1 Stream 5 Channel 7 dma.DMA_Channel = DMA_Channel_7; dma.DMA_PeripheralBaseAddr = (uint32_t)&DAC->DHR12R1; // Регистр данных ЦАП (12 бит, выравнивание вправо) dma.DMA_Memory0BaseAddr = (uint32_t)buffer; dma.DMA_DIR = DMA_DIR_MemoryToPeripheral; dma.DMA_BufferSize = size; dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Адрес ЦАП не меняется dma.DMA_MemoryInc = DMA_MemoryInc_Enable; // Адрес массива меняется dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16 бит dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; dma.DMA_Mode = DMA_Mode_Circular; // Циклический режим dma.DMA_Priority = DMA_Priority_High; dma.DMA_FIFOMode = DMA_FIFOMode_Disable; dma.DMA_MemoryBurst = DMA_MemoryBurst_Single; dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA1_Stream5, &dma); DMA_Cmd(DMA1_Stream5, ENABLE); } ``` #### `main.c` ```c #include "main.h" #include #define POINTS 100 // Количество точек в периоде #define PI 3.1415926 uint16_t DualSineWave[POINTS]; // Функция расчета сложной волны (две гармоники) void Calc_Wave(void) { for (int i = 0; i < POINTS; i++) { float t = (float)i / POINTS * 2 * PI; // V = sin(t) + 0.5 * sin(2t) // Нормируем результат, чтобы он был от 0 до 4095 // Исходный диапазон функции от -1.5 до 1.5 (примерно) // Смещаем на +1.5 -> диапазон 0..3 // Умножаем на 4095/3 -> диапазон 0..4095 float val = sin(t) + 0.5 * sin(2 * t); // Приведение к 12 битам ЦАП uint16_t dac_val = (uint16_t)((val + 1.5) * (4095.0 / 3.0)); // Ограничение на всякий случай if (dac_val > 4095) dac_val = 4095; DualSineWave[i] = dac_val; } } int main(void) { Calc_Wave(); // Заполняем массив значениями GPIO_Ini(); DMA_Ini(DualSineWave, POINTS); // Настраиваем DMA на наш массив DAC_Ini(); TIM_Ini(); // Запускаем таймер, который начнет "дергать" ЦАП while(1) { // Процессор свободен, генерация идет аппаратно через DMA } } ```