【正点原子STM32连载】 第三十四章 PWM DAC实验摘自【正点原子】STM32F103 战舰开发指南V1.2

news2024/11/25 7:54:29

1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html#

第三十四章 PWM DAC实验

前面的章节,我们介绍了STM32F1自带DAC模块的使用,虽然STM32F103ZET6具有内部DAC,但是也仅仅只有两条DAC通道,而STM32还有其它的很多型号是没有DAC的。通常情况下,采用专用的D/A芯片来实现,但是这样就会带来成本的增加。不过STM32所有的芯片都有PWM输出,并且PWM输出通道很多,资源丰富。因此,我们可以使用PWM + 简单的RC滤波来实现DAC的输出从而节省成本。
本章我们将向大家介绍如何使用STM32F1的PWM来设计一个DAC。我们将使用按键(或USMART)控制STM32F1的PWM输出,从而控制PWMDAC的输出电压,通过ADC1的通道1采集PWM DAC的输出电压,并在LCD模块上面显示ADC获取到的电压值以及PWM DAC的设定输出电压值等信息本章分为如下几个小节:
34.1 PWM DAC技术的实现原理
34.2 硬件设计
34.3 程序设计
34.4 下载验证

34.1 PWM DAC技术的实现原理

DAC工作过程是将源电压按照8位、12位、16位等分辨率进行分割,其输出的电压是最小精度LSB(即1/28、1/212、1/216等)的整数倍,这就是DAC输出的电压。
我们来分析一下PWM波形的特性。PWM信号可以被分解为一个直流分量和一个占空比固定,但是平均幅度为零的方波,如图34.1.1.1所示。
在这里插入图片描述

图34.1.1.1 PWM波形的等效分解
如果使PWM信号的占空比随时间改变,那么其直流分量随之改变,信号滤除交流分量后,将会输出幅度变化的模拟信号。因此通过改变PWM 信号的占空比,可以产生不同的模拟信号。这种技术称之为PWM DAC。
PWM是周期固定,占空比可调的数字信号。在实际电路中,典型的PWM波形,如图34.1.1.2所示。
在这里插入图片描述

图34.1.1.2 实际电路典型PWM波形图
下面根据高数与信号与系统课程的知识我们作一个简单的推导,感兴趣的同学可以查阅对应的知识,如果不感兴趣,直接跳过推导过程,看最后的结论即可。
我们可以把PWM波形用分段函数①表示出来。占空比可以用②的表达式来表示。
在这里插入图片描述

公式⑩正好验证了图34.1.1.1的PWM等效原理。由此我们可知PWM的输出波形为一个与占空比有关的直流等效信号,同时伴有多个不同频率的信号的叠加。如果能把这些频率信号尽可能过滤掉,那么我们通过调整PWM的占空比即可方便实现我们需要的DAC结果,即VDAC=(VH-VL)*p。
分辨率也是DAC一个重要的参数,它可以表示DAC输出的最小精度。存在两个主要误差源影响PWM方式DAC分辨率。首先,PWM信号的占空比只能表示有限的分辨率。这是因为STM32的PWM的占空比是输出比较寄存器CCRx与TIMx_CNT进行比较的结果,而CCRx在STM32F1系列中最多能设置为16位。那么很显然地,用PWM实现的DAC分辨率就与TIMx_CNT有关,即定时器的时钟频率越高则CCRx可以设置的值越多,分辨率相应地越高。但由于定时器最高时钟是72M,这也会导致分辨率越高,DAC的速度越慢。
第二个误差源是PWM信号中不期望的谐波分量产生的峰峰值。前面PWM的频域展开公式⑩说明PWM信号需要通过滤波器才能输出一个纹波较小的直流信号,但实际上对于简单设计的滤波器对交流信号的过滤能力是有限的,所以输出信号还会带有一定的交流成份。
根据公式⑧,将k=1代入我们可以算出PWM的一次谐波幅度:

当sin(πp-π)=1时滤波器需要达到衰减峰值,可知PWM占空比为50%时,一次谐波的幅度最大。为了减少这个基波的影响,我们希望滤波器在这个最大幅度下也能把基波的交流影响衰减到1/2LSB以下,即后外围滤波器至少需要满足以下条件才能避免DAC输出干扰过大:

根据公式可知=,当DAC为12位精度时,代入Y=12可知我们设计的滤波器需要衰减74dB以上,当为8位精度时,衰减需要达到50dB。
我们知道一阶RC电路截止频率计算公式为:

把电容等效成一个电阻,对于一阶分压时电压的等效衰减的表达式可以是:

根据以上公式就能很好地设计一个满足我们需要的滤波器参数了。为了实现低成本的RC电路,我们使用两个一阶RC电路串联起来作为滤波器。
STM32F103的定时器最快的计数频率是72Mhz,8位分辨率的时候,PWM频率为72M/256=281.25Khz。我们需要把交流信号至少衰减50dB左右。
34.2 硬件设计

  1. 例程功能
    我们将设计一个8位的DAC,使用按键(或USMART)控制STM32F1的PWM输出(占空比),从而控制PWM DAC的输出电压。为了得知PWM的输出电压,通过ADC1的通道1采集PWM DAC的输出电压,并在LCD模块上面显示ADC获取到的电压值以及PWM DAC的设定输出电压值等信息。
  2. 硬件资源
    1)LED灯
    LED0 – PB5
    2)独立按键
    KEY0 – PE4
    KEY_UP – PA0
    3)PWM输出通道
    TIM1的通道1,对应IO是PA8
  3. 原理图
    根据前面分析的原理,在硬件设计上就比较简单了。PWM可以由STM32定时器输出,我们只需要在外围增加一个滤波电路即可。我们使用的是RC滤波电路,其电路设计如下图所示:
    在这里插入图片描述

图25.2.1 PWM DAC连接原理设计
根据我们的设计,输出8位DAC时,经过一阶滤波后DAC输出的交流信号大概的衰减可以达到117dB,可见我们设计是符合要求的。
这里有个特别需要注意的地方:因为PWM_DAC和OV_VSYNC共用了PA8引脚,所以在做本实验的时候,不能插摄像头模块或OLED模块,否则可能会影响PWM转换结果。
如下图所示,本实验需要用短路帽将PDC和ADC排针连接起来。
在这里插入图片描述

图25.2.2 PCB对应PWM DAC的位置
34.3 程序设计
34.3.1 程序流程图
在这里插入图片描述

图34.3.1.1 PWM DAC实验程序流程图
34.3.2 程序解析

  1. PWM DAC驱动代码
    这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PWM DAC驱动源码包括两个文件:pwmdac.c和pwmdac.h。
    在pwmdac.h中,定义的宏定义如下:
    /*
  • PWMDAC 默认是使用 PA8, 对应的定时器为 TIM1_CH1, 如果你要修改成其他IO输出, 则相应
  • 的定时器及通道也要进行修改. 请根据实际情况进行修改.
 */
#define PWMDAC_GPIO_PORT             	GPIOA
#define PWMDAC_GPIO_PIN             	GPIO_PIN_8
/* PA口时钟使能 */
#define PWMDAC_GPIO_CLK_ENABLE()  	do{ __HAL_RCC_GPIOB_CLK_ENABLE();}while(0)

#define PWMDAC_TIMX                 	TIM1
#define PWMDAC_TIMX_CHY            	TIM_CHANNEL_1		/* 通道Y,  1<= Y <=4 */
#define PWMDAC_TIMX_CCRX          	PWMDAC_TIMX->CCR1 	/* 通道Y的输出比较寄存器 */
/* TIM1 时钟使能 */
#define PWMDAC_TIMX_CLK_ENABLE() 	do{ __HAL_RCC_TIM1_CLK_ENABLE();}while(0) 
下面介绍pwmdac.c文件的函数,首先是pwmdac_init函数,其定义如下:
/**
 * @brief     PWM DAC初始化, 实际上就是初始化定时器
 * @note
 *              定时器的时钟来自APB1 / APB2, 当APB1 / APB2 分频时, 定时器频率自动翻倍
 *              所以, 一般情况下, 我们所有定时器的频率, 都是72Mhz 等于系统时钟频率
 *              定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
 *              Ft = 定时器工作频率, 单位: Mhz
* @param      arr: 自动重装值。
 * @param      psc: 时钟预分频数
 * @retval     无
 */
void pwmdac_init(uint16_t arr, uint16_t psc)
{
    TIM_OC_InitTypeDef timx_oc_pwmdac = {0};

    PWMDAC_TIMX_CLK_ENABLE();                      /* PWM DAC 定时器时钟使能 */

    g_tim1_handle.Instance = TIM1;                /* 定时器1 */
    g_tim1_handle.Init.Prescaler = psc;          /* 定时器分频 */
    g_tim1_handle.Init.CounterMode = TIM_COUNTERMODE_UP;  /* 递增计数模式 */
g_tim1_handle.Init.Period = arr;             /* 自动重装载值 */
/* 使能TIMx_ARR进行缓冲 */
    g_tim1_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;   
    HAL_TIM_PWM_Init(&g_tim1_handle);            /* 初始化PWM */

    timx_oc_pwmdac.OCMode = TIM_OCMODE_PWM1;   /* CH1/2 PWM模式1 */
    timx_oc_pwmdac.Pulse = 0;                     /* 设置比较值,此值用来确定占空比 */
timx_oc_pwmdac.OCPolarity = TIM_OCPOLARITY_HIGH;  /* 输出比较极性为高 */
/* 配置TIM1通道1 */
    HAL_TIM_PWM_ConfigChannel(&g_tim1_handle, &timx_oc_pwmdac, PWMDAC_TIMX_CHY);
    HAL_TIM_PWM_Start(&g_tim1_handle, TIM_CHANNEL_1); /* 开启定时器1通道1 */
}

HAL_TIM_PWM_Init初始化TIM1并设置TIM1的ARR和PSC等参数,其次通过调用函数HAL_TIM_PWM_ConfigChannel设置定时器通道使用PWM1模式以及设置比较值等参数,最后通过调用函数HAL_TIM_PWM_Start来使能TIM1以及使能PWM通道TIM1_CH1输出。
HAL_TIM_PWM_Init会调用HAL_TIM_PWM_MspInit函数,用于存放GPIO、NVIC和时钟相关的代码。HAL_TIM_PWM_MspInit函数定义如下:

/**
 * @brief       定时器底层驱动,时钟使能,引脚配置
 * @note
 *               此函数会被HAL_TIM_PWM_Init()调用
 * @param       htim:定时器句柄
 * @retval      无
 */
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    GPIO_InitTypeDef gpio_init_struct;

    if (htim->Instance == TIM1)
    {
        __HAL_RCC_TIM1_CLK_ENABLE();                  /* 使能定时器1 */
        __HAL_AFIO_REMAP_TIM1_PARTIAL();             /* TIM1通道引脚部分重映射使能 */
        PWMDAC_GPIO_CLK_ENABLE();                     /* GPIO 时钟使能 */

        gpio_init_struct.Pin = PWMDAC_GPIO_PIN;
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;
        gpio_init_struct.Pull = GPIO_PULLUP;
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(PWMDAC_GPIO_PORT, &gpio_init_struct); 
    }
}

下面介绍pwmdac_set_voltage函数,该函数先计算得到比较值,然后通过设置比较值来改变占空比,从而控制PWM DAC输出的电压值,其定义如下:

/**
 * @brief       设置PWM DAC输出电压
 * @param       vol : 0~3300,代表0~3.3V
 * @retval      无
 */
void pwmdac_set_voltage(uint16_t vol)
{
  float temp = vol;
  temp /= 100;             	/* 缩小100倍, 得到实际电压值 */
  temp = temp * 256 / 3.3; 	/* 将电压转换成PWM占空比 */
  __HAL_TIM_SET_COMPARE(&g_tim1_handler, PWMDAC_TIMX_CHY, temp);/*设置新的占空比*/
}
最后在main函数里面编写如下代码: 
extern TIM_HandleTypeDef g_tim1_handler;

int main(void)
{
    uint16_t adcx;
    float temp;
    uint8_t t = 0;
    uint8_t key;
uint16_t pwmval = 0;

    HAL_Init();                             	/* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);	/* 设置时钟, 72Mhz */
    delay_init(72);                        	/* 延时初始化 */
    usart_init(115200);                   	/* 串口初始化为115200 */
    usmart_dev.init(72);                  	/* 初始化USMART */
    led_init();                             	/* 初始化LED */
    lcd_init();                             	/* 初始化LCD */
    key_init();                             	/* 初始化按键 */
    adc3_init();                            	/* 初始化ADC */
    pwmdac_init(256 - 1, 0);     /* PWM DAC初始化, Fpwm = 72M/256 =281.25Khz */
    lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30,  70, 200, 16, 16, "PWM DAC TEST", RED);
    lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "KEY_UP:+  KEY1:-", RED);
    lcd_show_string(30, 130, 200, 16, 16, "PWM VAL:", BLUE);
    lcd_show_string(30, 150, 200, 16, 16, "DAC VOL:0.000V", BLUE);
    lcd_show_string(30, 170, 200, 16, 16, "ADC VOL:0.000V", BLUE);
    while (1)
    {
        t++;
        key = key_scan(0);		/* 按键扫描 */
        if (key == WKUP_PRES) 	/* PWM占空比调高 */
        {
            if (pwmval < 250) 	/* 范围限定 */
            {
                pwmval += 10;
            }
/* 输出新的PWM占空比 */
            __HAL_TIM_SET_COMPARE(&g_tim1_handler, PWMDAC_TIMX_CHY, pwmval);
        }
        else if (key == KEY1_PRES)	/* PWM占空比调低 */
        {
            if (pwmval > 10)			/* 范围限定 */
            {
                pwmval -= 10;
            }
            else
            {
                pwmval = 0;
            }
/* 输出新的PWM占空比 */
             __HAL_TIM_SET_COMPARE(&g_tim1_handler, PWMDAC_TIMX_CHY, pwmval); 
        }
        if (t == 10 || key == KEY1_PRES || key == WKUP_PRES) 
        {  /* WKUP / KEY1 按下了, 或者定时时间到了 */
/* PWM DAC 定时器输出比较值 */
            adcx = __HAL_TIM_GET_COMPARE(&g_tim1_handler,PWMDAC_TIMX_CHY); 
            lcd_show_xnum(94, 130, adcx, 3, 16, 0, BLUE);	/* 显示CCRX寄存器值 */
            temp = (float)adcx * (3.3 / 256); 				/* 得到DAC电压值 */
            adcx = temp;
            lcd_show_xnum(94, 150, temp, 1, 16, 0, BLUE); 	/* 显示电压值整数部分 */
            temp -= adcx;
            temp *= 1000;
            lcd_show_xnum(110, 150, temp, 3, 16, 0X80, BLUE);/* 电压值的小数部分 */
            adcx = adc3_get_result_average(ADC3_CHY, 10); /* ADC3通道1的转换结果 */
            temp = (float)adcx * (3.3 / 4096);   /* 得到ADC电压值(adc是12bit的) */
            adcx = temp;
            lcd_show_xnum(94, 170, temp, 1, 16, 0, BLUE); /* 显示电压值整数部分 */
            temp -= adcx;
            temp *= 1000;
            lcd_show_xnum(110, 170, temp, 3, 16, 0X80, BLUE);/* 电压值的小数部分 */
            LED0_TOGGLE(); /* LED0闪烁 */
            t = 0;
        }
        delay_ms(10);
    }
}

main函数初始化了LED和LCD用于显示效果,初始化按键和ADC用于修改ADC的占空比,辅助显示ADC。
34.4 下载验证
下载代码后,LED0不停的闪烁,提示程序已经在运行了。此时,可以通过按下KEY_UP按键增大输出电压,按下KEY1按键则电压变小。
在这里插入图片描述

图34.4.1 TFTLCD显示效果图
注意:因为PWM_DAC和OV_VSYNC共用了PA8引脚,所以在做本例程的时候,不能插摄像头模块或OLED模块,否则可能会影响PWM转换结果!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/661766.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NSS刷web(1)

一点点做,简单的也不跳了,就当回忆知识了 [SWPUCTF 2021 新生赛]gift_F12 ctrlu [SWPUCTF 2021 新生赛]jicao [SWPUCTF 2021 新生赛]easy_md5 [SWPUCTF 2021 新生赛]easy_sql [SWPUCTF 2021 新生赛]include [SWPUCTF 2021 新生赛]easyrce [SWPUCTF 2021 新生赛]caidao [SWP…

PMP证书考下来要多少费用?

PMP考试形式分为&#xff1a;笔试、机考。PMP考试这里只着重介绍笔试&#xff08;大陆地区目前都是笔试&#xff09;&#xff1a; PMP认证考试在大陆内的考试一般一年举行四次&#xff0c;分别在3、6、9、12月份。2023年考试时间是3、5、8、11月份。 考试方式是笔试。考试改版…

【云原生】初识Kubernetes的理论基础

1.k8s的由来及其技术运用 1.1 k8s的简介 Kubernetes&#xff0c;词根源于希腊语的 舵手、飞行员。在国内又称k8s&#xff08;因为k和s之间有8个字母&#xff0c;所以得名。“国内程序员的幽默”&#xff09;。用于自动部署、扩展和管理“容器化&#xff08;containerized&…

面向锂电池行业实验室测试排配智能优化解决方案

获取更多资讯&#xff0c;赶快关注公众号&#xff08;《智能制造与智能调度》和》《商简智能》&#xff09;吧&#xff01; 文章目录 1 何为排配问题2 排配特点3 排配现状和痛点4 智能排配解决方案4.1 整体架构4.2 总体思路4.3 智能排配约束4.4 智能算法助力智能排配4.5 智能交…

RFID课程要点总结_3 Wireless Communication Principle of RFID

3. Wireless Communication Principle of RFID Different work principles of different carrier frequency 不同频率载波也适用不同的工作原则。 前面已经有所涉及&#xff0c;比如LF HF适用于近距离&#xff0c;UHF SHF适合远距离。 前者适用 Inductively Coupled RF Mod…

使用华为云AstroZero,不用一行代码,制作端午节加班申请模板

前言 6月22号就要端午了&#xff0c;很多公司会想统计下端午节加班的人数。传统的做法是用社交软件&#xff0c;谁要加班谁反馈&#xff0c;然后逐级汇总上传。大一点的企业&#xff0c;可能用自己的内部办公系统来申请。 但对于小企业来说&#xff0c;没有定制化的办公系统&a…

文件夹提示《文件或目录损坏》怎么解决

在经常使用电脑的过程中&#xff0c;大家平常会遇见一些困难&#xff0c;其中最常见的就是文件夹了&#xff0c;许多人就反映说自己遇见过文件夹《文件或目录损坏》打不开&#xff0c;那么遇见这个困难该怎么办呢&#xff1f;不知道的朋友赶紧瞧瞧小编整理的文件夹《文件或目录…

【参考设计】2KW AC/DC数字电源方案

什么是数字电源&#xff1f; 数字电源即通过一颗通用的数字信号控制器&#xff0c;完成传统模拟电源控制IC所具备的PWM、保护、环路补偿等功能&#xff0c;同时具备通信监控功能。 数字电源的数字信号控制器取代了模拟电源的控制芯片、环路补偿的运放及相关器件。软件处理的灵…

软考A计划-系统集成项目管理工程师-信息化知识(一)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

leetcode 225. 用队列实现栈

2023.6.19 这题使用队列来实现栈的功能&#xff0c;和栈实现队列稍有不同。pop操作的实现 需要将队列1的除队尾元素之外的所有元素移到队列2中&#xff0c;然后返回并移除队列1剩下的元素。 下面上代码&#xff1a; class MyStack {queue<int> que1;queue<int> qu…

How to Write and Publish a Scientific Paper-Approaching a Writing Project

contents Approaching a Writing ProjectESTABLISHING THE MINDSETPREPARING TO WRITEDOING THE WRITINGREVISING YOUR WORK Approaching a Writing Project ESTABLISHING THE MINDSET 也许最基本的是&#xff0c;请记住你的写作目的是为了传达信息&#xff0c;而不是为了给人…

Flutter悬浮UI的设计Overlay组件

文章目录 APP开发经常要遇到的开发场景Overlay 的介绍Overlay的使用规则举例说明源码例子报错报错No Overlay widget found报错原因解决方法 修改后的源码 例子效果 APP开发经常要遇到的开发场景 有时候我们在开发APP的时候会遇到下面这些需求&#xff1a; 在现有页面上添加浮…

选择题UI

选择题UI QuestionInfoSetting.cs using System; using System.Collections.Generic; using System.Threading.Tasks; using TMPro; using UnityEngine; using UnityEngine.UI;public class QuestionInfoSetting : MonoBehaviour {[Header("选项")]public GameObj…

LabVIEW错误-1073807360发生于 VISA Configure Serial Port (Instr).vi

调试上位机发生错误&#xff0c;错误信息为&#xff1a;“错误-1073807360发生于 VISA Configure Serial Port (Instr).vi->300W.vi中的属性节点&#xff08;arg 1&#xff09;” 查阅资料后得出的出错原因大致分为两种&#xff1a; 1.在运行程序时&#xff0c;没有选择端…

华为OD机试之MELON的难题(Java源码)

MELON的难题 题目描述 MELON有一堆精美的雨花石(数量为n&#xff0c;重量各异)&#xff0c;准备送给S和W。MELON希望送给俩人的雨花石重量一致&#xff0c;请你设计一个程序&#xff0c;帮MELON确认是否能将雨花石平均分配。 输入描述 第1行输入为雨花石个数: n&#xff0c;0&…

SN74LVC2T45使用说明

DIR高电平&#xff0c;允许从A传输到B&#xff0c;而低电平则允许从B传输到A。 针脚A和DIR以VCCA为基准&#xff0c;针脚B以VCCB为基准

PACS影像系统源码:三维后处理解决方案

一、三维后处理功能是临床数字技术中的重要组成部分&#xff0c;在与PACS系统整合后,能帮助医院构建完整的影像管理及三维影像后处理平台&#xff0c;实现在任意PACS终端进行三维影像后处理工作。 二、三维后处理解决方案是指对三维医学影像数据进行加工处理&#xff0c;以提取…

【C/C++套接字编程】UDP通信实验

目录 一、实验目的&#xff1a; 二、实验内容简要描述 三、实验步骤与结果分析 四、结论 系列博客 【C/C套接字编程】套接字的基本概念与基础语法_Mr_Fmnwon的博客-CSDN博客 【C/C套接字编程】TCP协议通信的简单实现_Mr_Fmnwon的博客-CSDN博客 【C/C套接字编程】UDP协议通信…

Springboot实现接口传输加解密

前言 先给大家看下效果&#xff0c;原本我们的请求是这样子的 加密后的数据传输是这样子的 加解密步骤&#xff1a; 1.前端请求前进行加密&#xff0c;然后发送到后端 2.后端收到请求后解密 3.后端返回数据前进行加密 4.前端拿到加密串后&#xff0c;解密数据 加解密算法&…

新增一个全局处理异常

要在Java应用程序中理新增一个全局处理异常&#xff0c;通常需要执行以下三个步骤&#xff1a; 1.定义全局异常处理器&#xff08;Global Exception Handler&#xff09;&#xff1a; 创建一个类&#xff0c;实现ExceptionHandler接口或使用ControllerAdvice注解&#xff0c;用…