提前声明:
本人才疏学浅,只是分享自己的学习历程,文章只有参考价值,其中的错误是无法避免的,但是功能都能实现,可私信我,我会实时更新. 使用的是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);
}
}
}
通过这样配置就基本能实现所有的功能,当然这样封装是有点丑的,有能力的可以使用指针函数去封装,这里只做基本演示