基与STM32F407zgt6的定时器(配合串口屏实时调节参数)发PWM波(可扫频)和DAC+DMA发正弦和三角波的学习历程

提前声明:

本人才疏学浅,只是分享自己的学习历程,文章只有参考价值,其中的错误是无法避免的,但是功能都能实现,可私信我,我会实时更新.           使用的是MDK keil          标准库

一.实现的效果

1.PWM波的点频模式和扫频模式(配合串口屏实时调节参数)

2.DAC+DMA的正弦波和三角波点频模式(配合串口屏实时调节参数)

二.实现的思路

1操作对象.

对于这个实时的操作的话,要先确定对上面参数进行修改,以下是示例

//UART.h
typedef struct {
    float Frequency;    //点频Hz  
    float startFre;     //扫频起始频率 
    float EndFre;       //截止频率
    float GOHZ;         //频率步进
    float TIHZ;         //步进时间
    float DUTY;         //占空比 
    float AMAM;         //幅值
    float IIPP;         //直流偏量
} DATA;

extern DATA g_Data;              // 数据存储结构体

需要实时修改的是这些变量,直接用extern 声明了方便跨文件使用。

然后加上一些枚举,来做为状态机来控制我们的发波函数只执行一次

#ifndef __USART_H
#define __USART_H

#include "stdio.h"
#include "stm32f4xx_conf.h"
#include "sys.h"

// 接收缓冲区大小定义
#define USART_REC_LEN   200   // 最大接收字节数
#define EN_USART1_RX    1     // 使能串口1接收(1-使能,0-禁止)

// 模式枚举(点频/扫频)
typedef void (*WaveHandler)(void);
typedef enum {
    mode_Spot,     // 点频模式
    mode_Sweep     // 扫频模式
} MODE;
typedef enum{
		no,
		mode,
		HZHZ,
		STHZ,
		ENHZ,
		goHZ,
		tiHZ,
		duTY,
		amAM,
		iiPP,
		wave
}CHANGE;
// 波形枚举(PWM/正弦/三角波)
typedef enum {
    wave_PWM,      // PWM波
    wave_Sin,      // 正弦波
    wave_angle  // 三角波
} WAVE;

// 数据存储结构体(包含所有变量)
typedef struct {
    float Frequency;      // 浮点变量HZHZ
    float startFre;      // 浮点变量SYHZ
    float EndFre;      // 浮点变量ENHZ
    float GOHZ;      // 浮点变量GOHZ
    float TIHZ;      // 浮点变量TIHZ
    float DUTY;      // 浮点变量DUTY
    float AMAM;      // 浮点变量AMAM
    float IIPP;      // 浮点变量IIPP
} DATA;

// 接收状态枚举
typedef enum {
   Waitstate,   // 等待开始帧
   Is_Rescive   // 接收数据帧
} State;

// 全局变量声明(供外部调用)
extern u8 USART_RX_BUF[USART_REC_LEN];       // 接收缓冲区
extern DATA g_Data;              // 数据存储结构体
extern State ReceiveState;          // 接收状态
extern u8 Start;                 // 当前开始帧('A'-'J')
extern u16 Length;                       // 当前接收数据长度
extern MODE g_mode;
extern WAVE g_wave;
extern CHANGE g_Change;
extern WaveHandler g_waveHandler;
// 函数声明
void uart_init(u32 bound);                   // 串口初始化(参数:波特率)
void Complete_Data(u8 startFrame,char* data, u16 length);  // 解析并存储数据

#endif

2串口修改.

我用的是陶晶驰3.5寸的串口屏,他家的产品再很多平台都有学习资源,可以去billbill和他的官网进修一下。先上部分的成品图

这个图像对于陶晶驰的软件USARTHIM来说很好配置。

这个的主要思路就是

串口屏被触屏->串口屏发送数据(你自己设置)->单片机串口接收->单片机解析数据并赋值给相应变量->变量数据被修改完成,示波器效果达成。

这里说一下我的思路,使用串口1,对于要修改的不同的值,编写不同的数据开头帧,‘A’-‘J’,分别对应上文几个操作对象

// 串口1初始化函数
void uart_init(u32 bound){
    // GPIO端口配置
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    // 使能时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);  // 使能GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 使能USART1时钟
 
    // 引脚复用映射
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);  // PA9复用为USART1_TX
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); // PA10复用为USART1_RX
    
    // 配置USART1引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;    // PA9和PA10
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;              // 复用功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;          // 速度50MHz
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;             // 推挽输出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;               // 上拉
    GPIO_Init(GPIOA, &GPIO_InitStructure);                     // 初始化GPIOA
    
    // 配置USART1参数
    USART_InitStructure.USART_BaudRate = bound;                // 波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 8位数据位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;     // 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(USART1, &USART_InitStructure);                  // 初始化USART1
    
    USART_Cmd(USART1, ENABLE);                                 // 使能USART1
    
#if EN_USART1_RX
    // 使能接收中断
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    
    // 配置USART1中断优先级
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;          // 串口1中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;  // 抢占优先级3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;         // 子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            // 使能中断通道
    NVIC_Init(&NVIC_InitStructure);                            // 初始化NVIC
#endif
}

void Complete_Data(u8 startFrame, char* data, u16 length) {
    char temp[20];
    // 限制数据长度,避免缓冲区溢出
    if (length >= sizeof(temp)) {
        length = sizeof(temp) - 1;
    }
    memcpy(temp, data, length);
    temp[length] = '\0';  // 字符串结束符
    
    // 根据开始帧匹配对应变量(直接赋值给全局变量)
    switch (startFrame) {
			case 'A': 
             if (strcmp(temp, "0") == 0) 
						{  // A0N → 点频模式
                g_mode = mode_Spot;
                g_Change =mode;               
             } 
						 else if (strcmp(temp, "1") == 0) 
						{  // A1N → 扫频模式
                g_mode = mode_Sweep; 
                g_Change = mode;
            }
            break;
            
        case 'B':  // Frequency(结构体成员g_Data.Frequency)
            g_Data.Frequency = atof(temp); g_Change=HZHZ;
            break;
            
        case 'C':  // startFre(结构体成员g_Data.startFre)
            g_Data.startFre = atof(temp); g_Change=STHZ;
            break;
            
        case 'D':  // EndFre(结构体成员g_Data.EndFre)
            g_Data.EndFre = atof(temp); g_Change=ENHZ;
            break;
            
        case 'E':  // GOHZ(结构体成员g_Data.GOHZ)
            g_Data.GOHZ = atof(temp); g_Change=goHZ;
            break;
            
        case 'F':  // TIHZ(结构体成员g_Data.TIHZ)
            g_Data.TIHZ = atof(temp); g_Change=tiHZ;
            break;
            
        case 'G':  // DUTY(结构体成员g_Data.DUTY)
            g_Data.DUTY = atof(temp); g_Change=duTY;
            break;
            
        case 'H':  // AMAM(结构体成员g_Data.AMAM)
            g_Data.AMAM = atof(temp); g_Change=amAM;
            break;
            
        case 'I':  // IIPP(结构体成员g_Data.IIPP)
            g_Data.IIPP = atof(temp); g_Change=iiPP;
            break;
            
       case 'J':  // 波形(J0N=PWM,J1N=正弦,J2N=三角)
            if (strcmp(temp, "0") == 0) {         // J0N → PWM波形
                g_wave = wave_PWM;
                g_Change = wave;
            } else if (strcmp(temp, "1") == 0) {  // J1N → 正弦波形
                g_wave = wave_Sin;
                g_Change = wave;
            } else if (strcmp(temp, "2") == 0) {  // J2N → 三角波形
                g_wave = wave_angle;
                g_Change = wave;
            }
            break;
    }
}

// 串口1中断服务函数
void USART1_IRQHandler(void) {
    u8 temp;  // 接收的字节数据
    
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {  // 接收中断
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);       // 清除中断标志
        temp = (u8)USART_ReceiveData(USART1);                 // 读取接收数据
        
        switch (ReceiveState) {
            case Waitstate:  // 等待开始帧
                // 开始帧为'A'-'J'
                if (temp >= 'A' && temp <= 'J') {
                    Start = temp;         // 记录当前开始帧
                    Length = 0;           // 重置数据长度
                    ReceiveState = Is_Rescive;  // 切换到接收状态
                }
                break;
                
            case Is_Rescive:  // 接收数据帧
                // 检查结束帧:MODE(A)和WAVE(J)的结束帧是'N',其他是'N'或'|'
                if ((temp == 'N') || 
                    ((temp == '|') && (Start != 'A' && Start != 'J'))) {
                    // 数据接收完成,解析并直接赋值给全局变量
                    Complete_Data(Start, (char*)USART_RX_BUF, Length);
                    ReceiveState = Waitstate;  // 回到等待状态
                } else {
                    // 存储数据(避免缓冲区溢出)
                    if (Length < USART_REC_LEN - 1) {
                        USART_RX_BUF[Length++] = temp;
                    } else {
                        // 缓冲区满,强制回到等待状态
                        ReceiveState = Waitstate;
                    }
                }
                break;
        }
    }
}

这样就实现的实时修改的问题了。

3.发波函数

以下是PWM,SIN,三角,三个波的点频函数,点频是扫频的基本,扫频是建立在点频上的。

    下面那个空!!!正弦数据表在右侧!!!

#include "timer1.h"
#include "stm32f4xx_dac.h"
#include "main.h"
#include "math.h"
#include "usart.h"
#include "stm32f4xx_tim.h"  
#include "delay.h"

#define DAC_DHR12R2_ADDRESS    0x40007414
#define DAC_DHR8R1_ADDRESS     0x40007410
#define PI 3.14159265358979323846f 
u32 SYSTEM_CLOCK = 168000000;  
DAC_InitTypeDef  DAC_InitStructure;
uint16_t sinePoints = 256; 
uint16_t aSine12bit[256] =           {
                                        2048, 2073, 2098, 2123, 2148, 2173, 2198, 2223,  
										2247, 2272, 2296, 2320, 2344, 2368,2391,2414,    
										2437, 2460, 2482, 2504, 2526, 2547, 2568, 2589,   
										2609, 2629, 2648, 2667, 2686, 2704, 2722, 2739,    
										2756, 2773, 2789, 2805, 2820, 2835, 2849, 2863,    
										2877, 2890, 2903, 2915, 2927, 2938, 2949, 2959,   
										2969, 2978, 2987, 2995, 3003, 3010, 3017, 3023,    
										3029, 3034, 3039, 3043, 3047, 3050, 3053, 3055,   
										3057, 3058, 3059, 3059, 3059, 3058, 3057, 3055,    
										3053, 3050, 3047, 3043, 3039, 3034, 3029, 3023,    
										3017, 3010, 3003, 2995, 2987, 2978, 2969, 2959,    
										2949, 2938, 2927, 2915, 2903, 2890, 2877, 2863,    
										2849, 2835, 2820, 2805, 2789, 2773, 2756, 2739,   
										2722, 2704, 2686, 2667, 2648, 2629, 2609, 2589,    
										2568, 2547, 2526, 2504, 2482, 2460, 2437, 2414,    
										2391, 2368, 2344, 2320, 2296, 2272, 2247, 2223,    
										2198, 2173, 2148, 2123, 2098, 2073, 2048, 2023,   
										1998, 1973, 1948, 1923, 1899, 1874, 1850, 1826,   
										1802, 1778, 1755, 1732, 1709, 1687, 1665, 1643,    
										1621, 1600, 1579, 1558, 1538, 1518, 1498, 1479,    
										1460, 1442, 1424, 1406, 1389, 1372, 1356, 1340,   
										1325, 1310, 1296, 1282, 1269, 1256, 1244, 1232,    
										1221, 1210, 1200, 1190, 1181, 1173, 1165, 1157,    
										1150, 1144, 1138, 1133, 1128, 1124, 1121, 1118,   
										1116, 1114, 1113, 1112, 1112, 1112, 1113, 1114,    
										1116, 1118, 1121, 1124, 1128, 1133, 1138, 1144,   
										1150, 1157, 1165, 1173, 1181, 1190, 1200, 1210,    
										1221, 1232, 1244, 1256, 1269, 1282, 1296, 1310,    
										1325, 1340, 1356, 1372, 1389, 1406, 1424, 1442,    
										1460, 1479, 1498, 1518, 1538, 1558, 1579, 1600,    
										1621, 1643, 1665, 1687, 1709, 1732, 1755, 1778,   
										1802, 1826, 1850, 1874, 1899, 1923, 1948, 1973 
                                                                                       };   

// 系统初始化
void systemInit(void)
{
   GPIO_InitTypeDef GPIO_InitStructure;

  /* DMA1 clock enable */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
  /* GPIOA clock enable (to be used with DAC) */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);                         
  /* DAC Periph clock enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  TIM6_Config();  
  DAC_DeInit(); 
}

void TIM6_Config(void)
{
  TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); 
  TIM_TimeBaseStructure.TIM_Period =2250-1;        //2250  
  TIM_TimeBaseStructure.TIM_Prescaler = 0;       
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;    
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
  TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);

  /* TIM6 TRGO selection */
  TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);
  
  /* TIM6 enable counter */
  TIM_Cmd(TIM6, ENABLE);
}
void Spot_Sin(float freq, float amplitude, float offset)
{
    /* 1. 调用系统初始化(确保外设就绪) */
    systemInit();
    
    /* 2. 参数合法性检查(限制在0~3.3V范围内) */
    float vref = 3.3f;  // DAC参考电压
    if (amplitude < 0) amplitude = 0;
    if (amplitude > vref) amplitude = vref;
    if (offset < 0) offset = 0;
    if (offset > vref) offset = vref;
    // 确保波形峰值不超过参考电压
    if ((offset + amplitude/2) > vref) {
        amplitude = 2 * (vref - offset);
        if (amplitude < 0) amplitude = 0;
    }
    
    /* 3. 生成正弦波数据(转换为DAC数字量) */
    float v2d = 4095.0f / vref;  // 电压转DAC值的系数(12位DAC)
    float amp_dac = (amplitude / 2) * v2d;  // 幅度对应的DAC半值
    float offset_dac = offset * v2d;        // 偏置对应的DAC值
    
    for (uint16_t i = 0; i < sinePoints; i++) {
        // 计算正弦值(0~2π)
        float sine_val = sin(2 * PI * i / sinePoints);
        // 转换为DAC输出值(0~4095)
        aSine12bit[i] = (uint16_t)(offset_dac + amp_dac * sine_val + 0.5f);
    }
    
    /* 4. 根据频率调整TIM6参数 */
    uint32_t apb1_clk = 84000000;  // APB1时钟频率(84MHz)
    uint32_t trigger_freq = freq * sinePoints;  // TIM6触发频率 = 频率 × 点数
    
    // 限制触发频率范围(避免溢出)
    if (trigger_freq == 0) trigger_freq = 1;  // 最小1Hz
    if (trigger_freq > apb1_clk) trigger_freq = apb1_clk;  // 最大不超过APB1时钟
    
    // 计算TIM6周期值(确保在16位范围内)
    uint32_t period = (apb1_clk / trigger_freq) - 1;
    if (period > 0xFFFF) period = 0xFFFF;  // 16位最大值
    TIM6->ARR = period;  // 动态更新TIM6周期
    
    /* 5. 配置DAC和DMA(复用原正弦波配置逻辑) */
    DMA_InitTypeDef DMA_InitStructure;
    DAC_InitTypeDef DAC_InitStructure;
    
    // 配置DAC通道2(PA5)
    DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;  // TIM6触发
    DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;  // 无内置波形
    DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
    DAC_Init(DAC_Channel_2, &DAC_InitStructure);
    
    // 配置DMA(传输正弦波数据到DAC)
    DMA_DeInit(DMA1_Stream6);  // 重置DMA流
    DMA_InitStructure.DMA_Channel = DMA_Channel_7;  // DAC通道2对应DMA通道7
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DAC_DHR12R2_ADDRESS;  // DAC通道2数据寄存器
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)aSine12bit;  // 正弦波数据地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;  // 内存到外设
    DMA_InitStructure.DMA_BufferSize = sinePoints;  // 传输数据量(点数)
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  // 外设地址不递增
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  // 内存地址递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  // 16位数据
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  // 循环模式(持续输出)
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init(DMA1_Stream6, &DMA_InitStructure);
    
    /* 6. 使能外设 */
    DMA_Cmd(DMA1_Stream6, ENABLE);        // 启动DMA传输
    DAC_Cmd(DAC_Channel_2, ENABLE);       // 使能DAC通道2
    DAC_DMACmd(DAC_Channel_2, ENABLE);    // 使能DAC的DMA功能
}
void Triangle_Spot(float freq, float amplitude, float offset)
{
 systemInit();  // 调用初始化

    // 参数合法性检查(参考别人的幅度范围)
    float vref = 3.3f;
    if (offset < 0) offset = 0;
    if (offset > vref) offset = vref;

    // 复用别人的幅度参数(1023是安全值)
    uint32_t triangle_amplitude = DAC_TriangleAmplitude_1023;  // 固定用1023
    uint16_t N = 1024;  // 1023对应2^10,N=1024

    // 计算TIM6周期(匹配频率)
    uint32_t apb1_clk = 84000000;
		uint32_t trigger_freq = freq * 2 * N;  // 三角波频率 = trigger_freq/(2*N)
		// 动态计算最优预分频系数(PSC),避免ARR溢出
		uint16_t psc = 0;
		if (trigger_freq < apb1_clk / 65536) {
		// 计算能使ARR不溢出的最小PSC
				psc = (apb1_clk / (trigger_freq * 65536)) - 1;
		}
		// 计算分频后的时钟
		uint32_t clk_div = apb1_clk / (psc + 1);
		uint32_t period = (clk_div / trigger_freq) - 1;

		// 确保period在有效范围内
		if (period > 0xFFFF) period = 0xFFFF;

		// 配置TIM6
		TIM6->PSC = psc;
		TIM6->ARR = period;

    // 复用别人的DAC配置
    DAC_InitTypeDef DAC_InitStructure;
    DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;
    DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_Triangle;
    DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = triangle_amplitude;  // 复用1023
    DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
    DAC_Init(DAC_Channel_2, &DAC_InitStructure);

    // 动态设置偏置(转换为12位DAC值)
    uint16_t offset_dac = (uint16_t)(offset * 4095 / vref + 0.5f);
    DAC_SetChannel2Data(DAC_Align_12b_R, offset_dac);  // 替代别人的固定0x100

    DAC_Cmd(DAC_Channel_2, ENABLE);  // 使能DAC
}
void TIM1_InitSpot(u32 freq, u8 duty) 
{
    TIM_Cmd(TIM1, DISABLE);
    TIM_DeInit(TIM1);
			// 核心:计算PSC和ARR(最大化频率范围)
		uint32_t cycles = SYSTEM_CLOCK / freq;  // 周期数

		// 计算分频系数PSC
		uint16_t psc = 0;
		if (cycles > 65536) {
				psc = cycles / 65536;  // 当周期数超过65536时,启用分频
		} else {
				psc = 0;  // 否则不分频,直接使用系统时钟
		}

		// 计算自动重装载值ARR
		uint16_t arr = (cycles / (psc + 1)) - 1;

		// 边界保护:确保PSC和ARR在合法范围内
		if (psc > 65535) {
				psc = 65535;  // 限制PSC最大值
		}

		if (arr < 1) {
				arr = 1;  // 确保ARR至少为1(避免定时器异常)
		} else if (arr > 65535) {
				arr = 65535;  // 限制ARR最大值
		}
    // 配置GPIO(PA8复用为TIM1_CH1)
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    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(GPIOA, &GPIO_InitStructure);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);

    // 配置定时器基础参数
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
    TIM_TimeBaseStructure.TIM_Period = arr;
    TIM_TimeBaseStructure.TIM_Prescaler = psc;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
    TIM_ARRPreloadConfig(TIM1, ENABLE);

    // 配置PWM输出
    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = (uint32_t)arr * duty / 100;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM1, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
		
		// 强制更新定时器(添加这两行)
    TIM1->EGR = TIM_EGR_UG;  // 生成更新事件
    TIM1->SR &= ~TIM_SR_UIF; // 清除更新标志
    
		// 启动定时器(高级定时器必须使能输出)
    TIM_CtrlPWMOutputs(TIM1, ENABLE);
    TIM_Cmd(TIM1, ENABLE);
}

扫频是很多个点频的轮番显示。所以扫频的思路就是通过配置一个定时器,用定时器的更新中断时来调用点频函数,以此来实现扫频。

用tim2来做更新中断,中断时间即是步进时间.

void Sweep_Init(void) 
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    // 调整预分频器为8399(84MHz / (8399+1) = 10kHz)
    // 每个计数对应100μs(0.1ms),扩大计时范围
    TIM_TimeBaseStructure.TIM_Prescaler = 8399;  // 84MHz / 8400 = 10kHz
    // 计算ARR:10kHz计数时,1ms对应10个计数
    uint32_t arr = (uint32_t)(g_Data.TIHZ * 10) - 1;  // 10kHz → 10计数/ms
    // 限制ARR最大值(16位)
    // 此时最大计时 = 65535 / 10 = 6553.5ms(约6.5秒),足够覆盖常见步进时间
    if (arr > 65535) arr = 65535;
    TIM_TimeBaseStructure.TIM_Period = arr;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);


    // 允许动态修改ARR
    TIM_ARRPreloadConfig(TIM2, DISABLE);

    // 配置中断
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure); 
    // 输出起始频率
    TIM1_InitSpot((u32)g_Data.startFre, (u8)g_Data.DUTY);
    
    // 启动定时器(自动开始扫频)
    TIM_Cmd(TIM2, ENABLE);
}

void TIM2_IRQHandler(void) 
{
     if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);

        // 1. 计算扫频方向
        float dir = (g_Data.startFre < g_Data.EndFre) ? 1.0f : -1.0f;

        // 2. 更新当前频率
        g_Data.Frequency += dir * g_Data.GOHZ;

        // 3. 边界检查:到达终点后自动回到起点
        if ((dir > 0 && g_Data.Frequency > g_Data.EndFre) || 
            (dir < 0 && g_Data.Frequency < g_Data.EndFre)) {
            g_Data.Frequency = g_Data.startFre;
        }
        uint16_t current_arr = TIM1->ARR;  // 获取当前的自动重装载值(频率由它和PSC决定)
				TIM1->CCR1 = (uint32_t)current_arr * g_Data.DUTY / 100;  // 仅更新占空比,不碰PSC/ARR
				// 4. 应用新频率和占空比
        
				if(g_wave==wave_PWM)TIM1_InitSpot((u32)g_Data.Frequency, (u8)g_Data.DUTY);
				if(g_wave==wave_Sin)Spot_Sin(g_Data.Frequency, g_Data.AMAM, g_Data.IIPP);
      
        uint32_t new_arr;
				new_arr = (uint32_t)(g_Data.TIHZ * 10) - 1;  
        if (new_arr > 65535) new_arr = 65535;
        TIM2->ARR = new_arr;
				
				
    }
}

---------------------------------------------------------------------------------------------------------------------------------

以上即使所有的功能函数了,接下来举例主函数

4.主函数演示

#include "main.h"
#include "delay.h"
#include "usart.h"
#include "timer1.h"
#include "led.h" 
#include "math.h"

int main(void)
{
	delay_init(168);		//延时初始化 
	uart_init(115200);	//串口初始化波特率为115200
	LED_Init();
	TIM1_InitSpot(1000,50);
	
	
	while (1)
  {  	
// debug	  if(g_Data.Frequency==500){GPIO_ResetBits(GPIOF,GPIO_Pin_9); }
				
			if(g_mode==mode_Sweep && g_wave==wave_PWM && g_Change==mode)
				{
					TIM_Cmd(TIM6,DISABLE);
					g_Change=no;
					Sweep_Init();
				}
				
				if(g_mode==mode_Spot && g_wave==wave_PWM && g_Change==mode)
				{
					g_Change=no;
					TIM_Cmd(TIM6,DISABLE);
					TIM_Cmd(TIM2,DISABLE);
					TIM1_InitSpot(1000,50);
				}
				
				if(g_mode==mode_Spot && g_wave==wave_PWM && g_Change==HZHZ)
				{
					TIM_Cmd(TIM6,DISABLE);
					TIM_Cmd(TIM2,DISABLE);
					g_Change=no;
					TIM1_UpdateDuty(g_Data.DUTY);TIM1_InitSpot(g_Data.Frequency,g_Data.DUTY); 
				}
				
				if(g_mode==mode_Spot && g_wave==wave_PWM && g_Change==duTY)
				{
					TIM_Cmd(TIM6,DISABLE);
					g_Change=no;
					TIM1_UpdateDuty(g_Data.DUTY);
			  }
				
				if(g_mode==mode_Spot& g_wave==wave_Sin && ((g_Change==HZHZ)||(g_Change==amAM)||(g_Change==iiPP)))
				{
					TIM_Cmd(TIM1,DISABLE);
					TIM_Cmd(TIM2,DISABLE);
					g_Change=no;
					Spot_Sin(g_Data.Frequency,g_Data.AMAM,g_Data.IIPP);	
				}
				
				if(g_mode==mode_Spot& g_wave==wave_angle  && ((g_Change==HZHZ)||(g_Change==amAM)||(g_Change==iiPP)))
				{
					TIM_Cmd(TIM1,DISABLE);
					TIM_Cmd(TIM2,DISABLE);
					g_Change=no;
					Triangle_Spot(g_Data.Frequency,g_Data.AMAM,g_Data.IIPP);	
				}
	
	}
}

通过这样配置就基本能实现所有的功能,当然这样封装是有点丑的,有能力的可以使用指针函数去封装,这里只做基本演示

### STM32F407定时器中断双通道PWM输出配置 #### CubeMX中的配置过程 在CubeMX中完成STM32F407的初始化设置,选择相应的外设并进行参数调整。对于需要实现的功能而言,重点在于TIM1高级定时器的选择及其工作模式设定。 - **硬件资源分配** - TIM1被指定为负责生成PWM信号的任务载体。 - GPIOA上的PA8PA9分别映射至TIM1_CH1N(互补通道)以及TIM1_BKIN(制动输入),而GPIOE上的PE13则对应于TIM1_CH1[^3]。 - **定时器功能选项卡内的具体操作** - 设置预分频系数(PSC),自动重装载值(ARR)以决定载频率。 - 启用Dead-Time插入机制,防止上下桥臂直通现象的生。 - 开启Complementary Output Compare Channel (CH1N), 并关联Break Input(BKIN)用于保护电路安全。 - 对应的中断服务例程(ISR)需勾选Enable Interrupts项以便响应溢出事件或特定条件下的捕捉/比较匹配情况。 ```c // 主函数入口处定义全局变量 uint16_t duty_cycle = 50; // 默认占空比初始值设为50% int main(void){ /* USER CODE BEGIN */ HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); //启动通道1 PWM 输出 HAL_TIMEx_PWMn_Start(&htim1, TIM_CHANNEL_1); //启动通道1N 补偿PWM 输出 while (1){ __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, ((duty_cycle * ARR_VALUE)/100)); // 更新CCR寄存器数值改变占空比 if(duty_cycle >= 100 || duty_cycle <= 0){ duty_cycle = abs(duty_cycle - 100); // 达到边界后反转方向 }else{ duty_cycle += DUTY_INCREMENT; // 动态调节增量步长 } osDelay(DLY_MS); } } ``` 上述代码展示了如何通过修改`__HAL_TIM_SET_COMPARE()`宏命令来动态更新PWM形的有效宽度,从而达到控制电机转速或其他负载的目的。同时,在主循环里实现了简单的线性扫频逻辑,使得占空比可以在一定范围内连续变化[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值