Генерация волновой формы: различия между версиями

Материал из MIK32 микроконтроллер
Нет описания правки
Нет описания правки
Строка 58: Строка 58:
     HAL_Timer16_Init(&htimer16_1);
     HAL_Timer16_Init(&htimer16_1);
}
}
</syntaxhighlight>Кроме этого в функции SystemClock_Config приведены настройки для тактирования. Убедитесь что в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_TIMER16_1_M. Сама функция должна выглядеть примерно так: <syntaxhighlight lang="c" line="1">
</syntaxhighlight>Кроме этого в функции SystemClock_Config приведены настройки для тактирования. Убедитесь что в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_TIMER16_1_M. Сама функция должна выглядеть примерно так:    
 
<syntaxhighlight lang="c" line="1">
void SystemClock_Config(void)
void SystemClock_Config(void)
{
{
    RCC_OscInitTypeDef RCC_OscInit = {0};
    RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};


    RCC_OscInit.OscillatorEnable = RCC_OSCILLATORTYPE_OSC32K | RCC_OSCILLATORTYPE_OSC32M;   
  RCC_OscInitTypeDef RCC_OscInit = {0};
    RCC_OscInit.OscillatorSystem = RCC_OSCILLATORTYPE_OSC32M;                           
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
    RCC_OscInit.AHBDivider = 0;                             
 
    RCC_OscInit.APBMDivider = 0;                             
  RCC_OscInit.OscillatorEnable = RCC_OSCILLATORTYPE_OSC32K | RCC_OSCILLATORTYPE_OSC32M;   
    RCC_OscInit.APBPDivider = 0;                             
  RCC_OscInit.OscillatorSystem = RCC_OSCILLATORTYPE_OSC32M;                           
    RCC_OscInit.HSI32MCalibrationValue = 0;                   
  RCC_OscInit.AHBDivider = 0;                             
    RCC_OscInit.LSI32KCalibrationValue = 0;
  RCC_OscInit.APBMDivider = 0;                             
    HAL_RCC_OscConfig(&RCC_OscInit);
  RCC_OscInit.APBPDivider = 0;                             
  RCC_OscInit.HSI32MCalibrationValue = 0;                   
  RCC_OscInit.LSI32KCalibrationValue = 0;
  HAL_RCC_OscConfig(&RCC_OscInit);
 
  PeriphClkInit.PMClockAHB = PMCLOCKAHB_DEFAULT;   
  PeriphClkInit.PMClockAPB_M = PMCLOCKAPB_M_DEFAULT | PM_CLOCK_WU_M | PM_CLOCK_PAD_CONFIG_M;   
  PeriphClkInit.PMClockAPB_P = PMCLOCKAPB_P_DEFAULT | PM_CLOCK_UART_0_M | PM_CLOCK_TIMER16_1_M;   
  PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_NO_CLK;
  PeriphClkInit.RTCClockCPUSelection = RCC_RTCCLKCPUSOURCE_NO_CLK;
  HAL_RCC_ClockConfig(&PeriphClkInit);


    PeriphClkInit.PMClockAHB = PMCLOCKAHB_DEFAULT;   
    PeriphClkInit.PMClockAPB_M = PMCLOCKAPB_M_DEFAULT | PM_CLOCK_WU_M | PM_CLOCK_PAD_CONFIG_M;   
    PeriphClkInit.PMClockAPB_P = PMCLOCKAPB_P_DEFAULT | PM_CLOCK_UART_0_M | PM_CLOCK_TIMER16_1_M;   
    PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_NO_CLK;
    PeriphClkInit.RTCClockCPUSelection = RCC_RTCCLKCPUSOURCE_NO_CLK;
    HAL_RCC_ClockConfig(&PeriphClkInit);
}
}
</syntaxhighlight>Для демонстрации вывода текста в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_UART_0_M. У вас его может не быть так как UART нужно включить отдельно. В начале main.c можно видеть объявление структуры с набором настроек для выбранного таймера, который использует функция инициализации Timer16_1_Init. <syntaxhighlight lang="c" line="1">
</syntaxhighlight>  
 
Для демонстрации вывода текста в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_UART_0_M. У вас его может не быть так как UART нужно включить отдельно. В начале main.c можно видеть объявление структуры с набором настроек для выбранного таймера, который использует функция инициализации Timer16_1_Init.    
 
<syntaxhighlight lang="c" line="1">
Timer16_HandleTypeDef htimer16_1;
Timer16_HandleTypeDef htimer16_1;


void SystemClock_Config(void);
void SystemClock_Config(void);
static void Timer16_1_Init(void);
static void Timer16_1_Init(void);
</syntaxhighlight>Перед инициализацией таймера (функция Timer16_1_Init) следует настроить вывод Ouiput в режим третьей функции вывода (последовательный интерфейс или таймер). Для Timer16_1 вывод Ouiput - Port0.10. Перевод вывода в третью функцию осуществляется с помощью регистра PORT_0_CFG (PAD_0_CFG). Убедитесь что в функции SystemClock_Config присутствует PMCLOCKAPB_M_DEFAULT или PM_CLOCK_PAD_CONFIG_M, которые отвечают за тактирование контроллера выводов. В дальнейшем вместо записи в регистры будет использоваться библиотека.  
</syntaxhighlight>  
 
Перед инициализацией таймера (функция Timer16_1_Init) следует настроить вывод Ouiput в режим третьей функции вывода (последовательный интерфейс или таймер). Для Timer16_1 вывод Ouiput - Port0.10. Перевод вывода в третью функцию осуществляется с помощью регистра PORT_0_CFG (PAD_0_CFG). Убедитесь что в функции SystemClock_Config присутствует PMCLOCKAPB_M_DEFAULT или PM_CLOCK_PAD_CONFIG_M, которые отвечают за тактирование контроллера выводов. В дальнейшем вместо записи в регистры будет использоваться библиотека.  


Далее нужно воспользоваться одной из функций для генерации сигнала нужной формы:
Далее нужно воспользоваться одной из функций для генерации сигнала нужной формы:

Версия от 14:30, 10 февраля 2023

В примере Timer16_1 на выводе таймера Output будет генерироваться сигнал волновой формы. Будут рассмотрены такие режимы как ШИМ, одноимпульсный, однократный.

Работа с конфигуратором (В разработке)

Для начала настроем в конфигураторе тактирование mik32, например, от внешнего кварца 32МГц. Затем настроем делители шины. Так как Timer16 тактируется от шины APB_P_CLK, то зададим делители AHB_DIV и APB_P_DIV. В данном примере оставим делители по умолчанию. В итоге вкладка с тактированием должна выглядеть так:

(Картинка тактирования из конфигуратора. В работе)

Затем перейдем к настройке самого таймера. Для этого откроем вкладку Timer16_1 и включим таймер, выбрав один из режимов. Выберем режим счетчика.

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

Далее следует выбрать нужный вам делитель частоты.

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

Следующая настройка - период. Это верхний предел, до которого считает таймер. В нашем примере таймер будет считать до максимального значения 65535.

Режим обновления регистров задает когда будут обновляться регистры ARR - значение автоматической перезагрузки (верхний предел счета таймера) и CMP - значение сравнения. Выберем, например, обновление после каждого доступа к записи.

Далее идут настройки триггера, которые в этом примере не нужны. Оставляем их значения по умолчанию.

После этого останется настроить цифровой фильтр от помех, если он нужен. При включении фильтра должен использоваться внутренний источник синхронизации.

В итоге настройки таймера в конфигураторе должны выглядеть примерно так:

(Объяснение работы с конфигуратором. В разработке)

Нажимаем кнопку генерации. В итоге у нас появится проект для PlatformIo. Далее работа идет в visual studio code.

Использование библиотеки HAL_Timer16

В сгенерированном проекте в файле main.c должна быть функция Timer16_1_Init, в которой будут заданы настройки для Timer16_1. Выглядит она так:

static void Timer16_1_Init(void)
{
    htimer16_1.Instance = TIMER16_1;

    /* Настройка тактирования */
    htimer16_1.Clock.Source = TIMER16_SOURCE_INTERNAL_SYSTEM;
    htimer16_1.CountMode = TIMER16_COUNTMODE_INTERNAL;
    htimer16_1.Clock.Prescaler = TIMER16_PRESCALER_1;
    htimer16_1.ActiveEdge = TIMER16_ACTIVEEDGE_RISING;

    /* Настройка верхнего предела счета */
    htimer16_1.Period = 0xFFFF;
    /* Настрйока режима обновления регистра ARR и CMP */
    htimer16_1.Preload = TIMER16_PRELOAD_AFTERWRITE;

    /* Настройка триггера */
    htimer16_1.Trigger.Source = TIMER16_TRIGGER_TIM1_GPIO1_9; 
    htimer16_1.Trigger.ActiveEdge = TIMER16_TRIGGER_ACTIVEEDGE_SOFTWARE;
    htimer16_1.Trigger.TimeOut = TIMER16_TIMEOUT_DISABLE;

    /* Настройки фильтра */
    htimer16_1.Filter.ExternalClock = TIMER16_FILTER_NONE;
    htimer16_1.Filter.Trigger = TIMER16_FILTER_NONE;

    /* Настройка режима энкодера */
    htimer16_1.EncoderMode = TIMER16_ENCODER_DISABLE;

    HAL_Timer16_Init(&htimer16_1);
}

Кроме этого в функции SystemClock_Config приведены настройки для тактирования. Убедитесь что в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_TIMER16_1_M. Сама функция должна выглядеть примерно так:

void SystemClock_Config(void)
{

   RCC_OscInitTypeDef RCC_OscInit = {0};
   RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

   RCC_OscInit.OscillatorEnable = RCC_OSCILLATORTYPE_OSC32K | RCC_OSCILLATORTYPE_OSC32M;   
   RCC_OscInit.OscillatorSystem = RCC_OSCILLATORTYPE_OSC32M;                          
   RCC_OscInit.AHBDivider = 0;                             
   RCC_OscInit.APBMDivider = 0;                             
   RCC_OscInit.APBPDivider = 0;                             
   RCC_OscInit.HSI32MCalibrationValue = 0;                  
   RCC_OscInit.LSI32KCalibrationValue = 0;
   HAL_RCC_OscConfig(&RCC_OscInit);

   PeriphClkInit.PMClockAHB = PMCLOCKAHB_DEFAULT;    
   PeriphClkInit.PMClockAPB_M = PMCLOCKAPB_M_DEFAULT | PM_CLOCK_WU_M | PM_CLOCK_PAD_CONFIG_M;     
   PeriphClkInit.PMClockAPB_P = PMCLOCKAPB_P_DEFAULT | PM_CLOCK_UART_0_M | PM_CLOCK_TIMER16_1_M;    
   PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_NO_CLK;
   PeriphClkInit.RTCClockCPUSelection = RCC_RTCCLKCPUSOURCE_NO_CLK;
   HAL_RCC_ClockConfig(&PeriphClkInit);

}

Для демонстрации вывода текста в PeriphClkInit.PMClockAPB_P присутствует PM_CLOCK_UART_0_M. У вас его может не быть так как UART нужно включить отдельно. В начале main.c можно видеть объявление структуры с набором настроек для выбранного таймера, который использует функция инициализации Timer16_1_Init.

Timer16_HandleTypeDef htimer16_1;

void SystemClock_Config(void);
static void Timer16_1_Init(void);

Перед инициализацией таймера (функция Timer16_1_Init) следует настроить вывод Ouiput в режим третьей функции вывода (последовательный интерфейс или таймер). Для Timer16_1 вывод Ouiput - Port0.10. Перевод вывода в третью функцию осуществляется с помощью регистра PORT_0_CFG (PAD_0_CFG). Убедитесь что в функции SystemClock_Config присутствует PMCLOCKAPB_M_DEFAULT или PM_CLOCK_PAD_CONFIG_M, которые отвечают за тактирование контроллера выводов. В дальнейшем вместо записи в регистры будет использоваться библиотека.

Далее нужно воспользоваться одной из функций для генерации сигнала нужной формы:

  • HAL_Timer16_StartPWM - ШИМ сигнал;
  • HAL_Timer16_StartOneShot - одноимпульсный сигнал;
  • HAL_Timer16_StartSetOnes - однократный сигнал.

В каждой из этих функции есть три аргумента: адрес структуры с настройками таймера, верхнее предельное значение счетчика таймера (период), значение сравнение, при котором будет менять свое состояние сигнал на выводе Output. Период всегда должен быть больше значения сравнения.

Запустим, например, ШИМ сигнал. Функция main будет выглядеть так:

int main()
{    

    SystemClock_Config();

    /**************************Включить вывод Output для Timer16_1**************************/
    /* Port0.9 */
    PAD_CONFIG->PORT_0_CFG |= (PORT_AS_TIMER << (2 * TIMER16_1_OUT)); 
    /***************************************************************************************/

    Timer16_1_Init();

    /********************************Генерация волновой формы********************************/
    HAL_Timer16_StartPWM(&htimer16_1, 0xFFFF, 0xFFFF/2);
    //HAL_Timer16_StartOneShot(&htimer16_1, 0xFFFF, 0xFFFF/2);
    //HAL_Timer16_StartSetOnes(&htimer16_1, 0xFFFF, 0xFFFF/2);
    /****************************************************************************************/
    
    while (1)
    {    

    }
       
}

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