前言 感谢正点原子和电子发烧友论坛提供的这次机会让我有机会体验这款正点原子STM32MP257开发板,希望可以借这个机会好好学习一下。 项目实现 项目通过识别我需要当成非陌生人的人脸,再使用LBPH人脸识别器(cv2.face.LBPHFaceRecognizer_create())训练为临时模型使用。剩下就是吧LBPH人脸识别器用作模型识别是否为陌生人,再通过蜂鸣器警报。 代码 import cv2 import numpy as np import gpiod import time # 创建LBPH脸识别器 recognizer = cv2.face.LBPHFaceRecognizer_create() # 创建人脸检测器 face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades+\'haarcascade_frontalface_default.xml\') # 蜂鸣器 GPIO 配置 LINE = 13 # 使用gpiod.request_lines配置GPIO with gpiod.request_lines( \"/dev/gpiochip5\", consumer=\"beep-example\", config={ LINE: gpiod.LineSettings( direction=gpiod.line.Direction.OUTPUT, output_value=gpiod.line.Value.INACTIVE ) }, ) as request: def get_reference_image_features(reference_image_path): img = cv2.imread(reference_image_path) if img is None: print(f\"Error: 无法加载参考图片 {reference_image_path}\") return None gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)) if len(faces) == 0: print(\"Error: 参考图片中未检测到人脸\") return None (x, y, w, h) = faces[0] face_roi = gray[y:y+h, x:x+w] return face_roi def train_recognizer_with_reference(reference_image_path): reference_features = get_reference_image_features(reference_image_path) if reference_features is None: return False labels = np.array([0]) recognizer.train([reference_features], labels) return True def real_time_recognition(reference_image_path): if not train_recognizer_with_reference(reference_image_path): print(\"Error: 人脸识别器训练失败\") return cap = cv2.VideoCapture(7) stranger_detected = False while True: ret, frame = cap.read() if not ret: print(\"Error: 无法读取摄像头帧\") break gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)) current_stranger_status = False for (x, y, w, h) in faces: face_roi = gray[y:y+h, x:x+w] label, confidence = recognizer.predict(face_roi) if confidence > 85: cv2.putText(frame, \"Stranger\", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2) cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 2) current_stranger_status = True else: cv2.putText(frame, \"Known Person\", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2) cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) cv2.imshow(\'Real-Time Recognition\', frame) if current_stranger_status and not stranger_detected: stranger_detected = True elif not current_stranger_status and stranger_detected: stranger_detected = False if stranger_detected: request.set_value(LINE, gpiod.line.Value.ACTIVE) time.sleep(1) request.set_value(LINE, gpiod.line.Value.INACTIVE) time.sleep(1) else: request.set_value(LINE, gpiod.line.Value.INACTIVE) if cv2.waitKey(1) & 0xFF == ord(\'q\'): break cap.release() cv2.destroyAllWindows() if __name__ == \"__main__\": reference_image_path = \"reference_image.jpg\" real_time_recognition(reference_image_path) 效果:在底部 总结 STM32MP257是一个很不错的芯片,在AI方面表现突出,再与正点原子合作,将STM32MP257的外设添加,让其的能力能发挥出来。
2025-07-06 17:09
本次视频录制测试,使用板载的摄像头进行视频录制,使用板载的按键进行视频录制的开始和停止。 from media.mp4format import * import os import utime from machine import Pin from machine import FPIOA # 视频参数 width = 800 height = 480 file_name = \"/data/record.mp4\" MAX_RECORD_TIME = 10# 秒 # 按键设置 fpioa = FPIOA() fpioa.set_function(21, FPIOA.GPIO21)# 按键GPIO21 KEY = Pin(21, Pin.IN, Pin.PULL_UP) def mp4_muxer_test(): print(\"等待按键按下开始录制...\") # 等待按键按下 while KEY.value() == 1: utime.sleep_ms(50) print(\"按键按下,开始录制\") # 实例化mp4 container mp4_muxer = Mp4Container() mp4_cfg = Mp4CfgStr(mp4_muxer.MP4_CONFIG_TYPE_MUXER) if mp4_cfg.type == mp4_muxer.MP4_CONFIG_TYPE_MUXER: mp4_cfg.SetMuxerCfg(file_name, mp4_muxer.MP4_CODEC_ID_H265, width, height, mp4_muxer.MP4_CODEC_ID_G711U) # 创建mp4 muxer mp4_muxer.Create(mp4_cfg) # 启动mp4 muxer mp4_muxer.Start() start_time_ms = utime.ticks_ms()# 记录开始时间 frame_count = 0 recording = True try: while recording: os.exitpoint() # 处理视频帧 mp4_muxer.Process() frame_count += 1 if frame_count % 10 == 0: print(f\"帧数: {frame_count}\") # 检查按键是否松开 if KEY.value() == 1: print(\"按键松开,停止录制\") recording = False # 检查最大录制时间 elapsed_time = utime.ticks_ms() - start_time_ms if elapsed_time >= MAX_RECORD_TIME * 1000: print(\"达到最大录制时间\") recording = False utime.sleep_ms(10) except BaseException as e: print(\"录制出错:\", e) # 停止并清理 mp4_muxer.Stop() mp4_muxer.Destroy() print(f\"录制完成,保存视频: {file_name}\") print(f\"总帧数: {frame_count}, 时长: {elapsed_time//1000}秒\") if __name__ == \"__main__\": os.exitpoint(os.EXITPOINT_ENABLE) # 主循环,可多次录制 while True: mp4_muxer_test() print(\"等待5秒后可再次录制...\") utime.sleep(5) 效果,按下按键开始录制,停止后停止录制,录制完成后保存。 查看data路径下的文件,如下所示,视频录制成功:
2025-07-06 15:21
接上回,这次说一说第三阶段的进展,一直在想如何将cnn融合进高云以后的视频案例里面,比如:下图 上面图中文件之间的关系应该是这样的: 一、 完整的视频数据处理流程 根据这些文件名,我们可以清晰地勾勒出整个数据流的路径。这是一个典型的FPGA图像处理系统架构: 输入端 (Camera Input) -> ISP (图像信号处理) -> DDR3 缓存 (Frame Buffer) -> 显示输出 (Display Output) 下面是每个阶段涉及的模块和具体工作: 阶段一:摄像头数据接收与解码 (MIPI/LVDS Input) 物理层: 数据首先从摄像头传感器通过高速差分接口(如MIPI或LVDS)进入FPGA。 src\\\\MIPIRx1LaneFre.v: 这是一个MIPI D-PHY接收器,负责接收MIPI协议的物理层信号。 src\\\\lvds_seders\\\\ddio_lvds.v: 这是一个LVDS接收器(Serializer/Deserializer),负责接收LVDS信号。 协议层: 接收到的串行数据需要被解码成并行的像素数据。 src\\\\MIPI\\\\mipi_dsi_csi2_rx\\\\mipi_dsi_csi2_rx.v: 核心的MIPI CSI-2协议解码模块,将MIPI数据包解析成像素行和帧。 src\\\\MIPI\\\\mipi_byte_to_pixel_converter\\\\mipi_byte_to_pixel_converter.v: 将解码后的字节流转换为像素格式。 src\\\\MIPI\\\\mipi_rx_advance\\\\mipi_rx_advance.v: 可能是一些高级的MIPI接收控制逻辑。 输出: 这个阶段的最终输出是原始的、未经处理的Bayer RAW格式的像素数据流。 阶段二:图像信号处理 (ISP - Image Signal Processing) 原始的Bayer数据是不能直接观看的,必须经过一系列复杂的ISP算法处理,才能变成我们肉眼看到的彩色图像。 Bayer到RGB转换 (Demosaicing): src\\\\isp\\\\bayer2rgb\\\\Line_Shift_RAM_8Bit.v: 这是一个行缓存。Bayer转RGB算法(如插值)需要同时访问相邻几行的数据,这个RAM就是用来缓存前几行像素的。 src\\\\isp\\\\bayer2rgb\\\\VIP_RAW8_RGB888.v: 核心的Bayer转RGB模块,将单色的Bayer模式(R-G-G-B)转换为全彩色的RGB888格式。 色彩校正 (Color Correction): src\\\\isp\\\\bayer2rgb\\\\VIP_Matrix_Generate_3X3_8Bit.v: 这是一个3x3矩阵乘法器。它用于进行色彩校正(CCM - Color Correction Matrix),修正因传感器和镜头导致的颜色偏差,使颜色看起来更自然。 图像几何变换: src\\\\isp\\\\FrameBoundCrop.v 和 src\\\\isp\\\\Sensor_Image_XYCrop.v: 这两个模块用于图像裁剪(Cropping),可以切掉图像边缘的无效区域,或者提取感兴趣的区域(ROI - Region of Interest)。 输出: 经过ISP流水线处理后,我们得到了可以直接显示的RGB格式的图像数据流。 阶段三:DDR3帧缓存 (Frame Buffering) 视频数据流的速度和显示刷新的速度往往不匹配,或者后续处理需要访问整帧图像,因此需要一个大容量的DDR3内存作为帧缓冲。 src\\\\axi\\\\axi4_ctrl.v: 核心的AXI控制器(我们在上一问中详细分析过)。它负责将ISP处理完的RGB图像数据流写入DDR3内存,并在需要时再从DDR3中读出。 src\\\\DDR3\\\\DDR3ControllerGWTopLite.v: DDR3内存控制器IP核。它将AXI总线协议转换为DDR3芯片能理解的底层读写命令。 src\\\\DDR3\\\\R0_FIFO_128... 和 src\\\\DDR3\\\\W0_FIFO_128...: 用于AXI控制器和DDR3控制器之间数据通路的异步FIFO,处理跨时钟域问题。 src/DDR3/DDR_Tick_Generator.v 和 src/DDR3/Reset.v: 为DDR3控制器提供时钟和复位信号。 工作流程: ISP输出的RGB数据流被axi4_ctrl.v写入DDR3中的某个帧缓冲区。当一帧写完后,可以通知显示控制器来读取。 阶段四:显示输出 (Display Output) src\\\\lcd_display\\\\lcd_driver.v 和 src\\\\lcd_display\\\\lcd_para.v: LCD显示驱动模块,负责从DDR3中(通过axi4_ctrl.v)读取帧数据,并生成LCD屏幕所需要的特定时序信号(如HSYNC, VSYNC, DE)。 src\\\\hdmi_ip\\\\rgb2dvi.v, src\\\\hdmi_ip\\\\tmds_channel.v, src\\\\hdmi_ip\\\\tmds_enc_rtl.v: HDMI输出模块。如果目标是HDMI显示器,这些模块会将RGB数据编码成TMDS差分信号,通过HDMI接口发送出去。 PLL模块 (src\\\\PLL...): 为系统中的不同部分(如DDR控制器、HDMI输出)提供所需的各种频率的时钟。 这就要求我对每个文件的逻辑都比较熟悉?看到这么多文件,我觉得要弄明白要花一点时间,同时还要将cnn拼接到合适的模块之间?我根据之前的思考,再借助一点大模型,我觉得这里的方案可以试一试: 在哪个阶段介入CNN能更好地实现图像处理? 这是一个非常关键的架构决策问题。根据处理任务的不同,CNN可以介入在不同的阶段,但有一个最佳的介入点**。** 方案A:在ISP之后,DDR3之前(最佳方案 ✅) 位置: 将CNN处理模块插入到 ISP流水线 和 axi4_ctrl.v的写通道 之间。 数据流: ISP -> (清晰的RGB图像) -> CNN模块 -> (处理后的图像或特征) -> AXI控制器 -> DDR3 为什么这是最佳方案? 处理高质量图像: ISP已经完成了去噪、白平衡、色彩校正等基础工作,输出的RGB图像质量最高,最适合作为CNN的输入,能获得最高的识别/分析准确率。 流式处理(Streaming): 这种“在线(On-the-fly)”的处理方式可以实现极低的延迟。ISP每输出一个像素或一个像素块,CNN模块就可以立即开始处理,无需等待整帧图像被写入DDR。这对于实时应用(如自动驾驶、工业检测)至关重要。 节省DDR带宽: 如果CNN的任务是目标检测或分类**,它输出的只是少量元数据(如坐标、类别),我们只需要将这些元数据写入DDR,而无需存储整帧原始图像,极大地节省了DDR带宽和容量。** 如果CNN的任务是图像增强或风格迁移**(Image-to-Image),它会输出一帧新的图像。将处理后的图像直接写入DDR,也只占用一次写带宽。** 易于实现ROI: ISP中的裁剪模块可以先提取出感兴趣区域(ROI),然后只将ROI部分送入CNN,减少了CNN的计算量。 方案B:在DDR3之后,显示之前 位置: CNN模块从DDR3中读取图像,处理后再写回DDR3,或者直接送给显示控制器。 数据流: ISP -> AXI -> DDR3 -> AXI -> CNN模块 -> DDR3/Display 缺点: 高延迟: 必须等待至少一整帧图像被完全写入DDR后,CNN才能开始读取和处理。这引入了至少一帧的延迟。 带宽瓶颈: 这个方案至少需要一次DDR写(ISP写入)和一次DDR读(CNN读取),占用了双倍的DDR带宽。如果处理结果还要写回DDR,就需要三次DDR访问,带宽很容易成为瓶颈。 架构复杂: 需要更复杂的DMA控制器来调度CNN对DDR的访问。 这里有2哥方案,不管哪一种,我都需要先弄明白这个模块之间的拼图,所以要看源码?另外要看数据怎么接入cnn的? 所以下面就是最近的工作:*附件:VF-G60K225_引脚定义-20250117-V1.0.xlsx 从这里面想了解一下不同模块的物理引脚分配情况 还有这个文件,正在进行模块拼接,由于信号太多,并且还不是很熟悉,所以还有很多事情要做?*附件:02-2_SC2210_DDR3_LVDS_1024600.xlsx 好吧,时间过的真快,我真想早点把这些弄明白
2025-07-06 15:18
测试远程访问开发板界面 一、安装vncserver软件 user@starfive:~$ sudo apt install xfce4 xfce4-goodies user@starfive:~$ sudo apt install tightvncserver 二、运行软件 2.1、创建登录密码 user@starfive:~$ vncserver 2.2、修改xstartup文件 user@starfive:~$ vi .vnc/xstartup 在文件最后增加startxfce4 & 三、远程桌面登录 3.1、选择VNC方式登录 3.2、输入创建的密码 3.3、登录后的界面 3.4、修改分辨率 默认显示分辨率有点低,登录时可以设置显示的分辨率 关闭掉上次打开的进程 user@starfive:~$ vncserver -kill :1 以1920*1080分辨率打开 user@starfive:~$ vncserver -geometry 1920x1080
2025-07-06 08:47
我想,高压电路跳闸或故障,维修公司是如何快速知道的?是用户电话联系,还是电力监控系统的异常告警?是哪些参数说明线路出故障了?响应时间是多久?多远距离内? 根据您的提问,高压电路故障的发现机制、诊断参数及响应流程可综合归纳如下(结合电力系统运作逻辑与搜索结果中维修服务特征): 一、故障发现机制:双重触发 电力监控系统自动告警 高压电网配备SCADA(数据采集与监控系统)、PMU(同步相量测量单元)等实时监测设备,通过以下参数突变触发告警: 电流/电压异常:短路时电流骤增(超过保护定值),接地故障时电压不平衡; 绝缘电阻下降:监测系统检测线路对地绝缘值异常(如低于1MΩ),预示漏电风险; 零序电流突变:三相电流矢量和不为零,提示接地故障。 系统自动定位故障区段(误差<300米),并向调度中心推送告警信息。 用户主动报修 对于未覆盖智能监控的末端线路(如老旧小区高压配电箱),用户通过维修公司的24小时客服电话(如网页中的400电话、广州13637297582等)反馈跳闸停电情况。 ? 二、核心诊断参数与故障类型对应表 监测参数 异常表现 对应故障类型 来源依据 电流值 瞬时骤增(超阈值) 短路(相间/对地) 绝缘电阻 持续下降或归零 线路老化、潮湿漏电 零序电流 三相不平衡(矢量和≠0) 单相接地、设备漏电 温度 局部过热(红外检测) 接触不良、过载 (行业通用标准补充) 功率波动 无功功率突增、功率因数异常 设备击穿、电容故障 ⏱️ 三、响应时间与服务半径 响应时效 城市核心区:智能监控系统告警后,维修团队30分钟内出发,优先处理高压主干线故障(网页)。 服务覆盖距离 专业高压维修公司通常覆盖同城50公里半径(如南京聚荣网服务全市),跨区域故障由电力公司分级联动处置。 偏远地区受限于交通,响应可能延长至4-6小时。 ? 四、维修流程关键步骤 断电验电:高压操作必先切断电源,验明无电并挂接地线(安全核心,网页; 更换部件:烧毁的空气开关、老化电缆、受损绝缘子等直接更换(网页),通常在2小时内响应城区故障。建议用户记录就近维修电话(如中的400客服)以备应急。
2025-07-06 08:28
这个小小的模块我调试了 4个小时以上, APP无效, 微信“易微联”可用, 按说明书来, 刚开机均匀闪烁, 长按至快闪3停1, 扫描设备, 连接, 选择WIFI, 连接,
2025-07-06 08:19
想问一下大家为什么我通过H桥驱动电路输出的扩频信号在经过变压器输出后会有这种高频震荡的情况,正常应该是方波的?
2025-07-06 07:50
各位大神 这是一个氧气传感器的转接板。 sensor是一个氧气传感器。 VCC为3V. 请问一下谁能解析一下这个运放的原理吗,若是放大器的话,是放大多少倍?
2025-07-06 01:35
光照传感器在对环境的光线情况的检测中起着重要的作用,这里所采用的是数字式光照传感器BH1750,它具有集成度高和检测精度高的特点。 该传感器以I2C接口的方式工作,其引脚的连接关系为: SCL------PA0 SDA------PA1 为此,对所用引脚的配置函数为: void BH1750_Init(void) { GPIOA_ModeCfg( GPIO_Pin_4, GPIO_ModeOut_PP_5mA ); GPIOA_ModeCfg( GPIO_Pin_0, GPIO_ModeOut_PP_5mA ); GPIOA_ModeCfg( GPIO_Pin_1, GPIO_ModeOut_PP_5mA ); } 为模拟I2C方式数据的需要,对输出高低电平的语句定义为: #define SCL_Set() GPIOA_SetBits(GPIO_Pin_0) #define SCL_Clr() GPIOA_ResetBits(GPIO_Pin_0) #define SDA_Set() GPIOA_SetBits(GPIO_Pin_1) #define SDA_Clr() GPIOA_ResetBits(GPIO_Pin_1) 其数据引脚读取输入信号的语句为: #define IIC_SDA_INGPIOA_ReadPortPin(GPIO_Pin_1) 与显示器件不同,传感器这类器件不仅是输出数据,还要读取数据,因此其在工作过程中是要不断切换工作模式的。 将数据引脚设置为输入模式的函数为: void IIC_INPUT_MODE_SET() { GPIOA_ModeCfg( GPIO_Pin_1, GPIO_ModeIN_PU); } 而将数据引脚设置为输出模式的函数则为: void IIC_OUTPUT_MODE_SET() { GPIOA_ModeCfg( GPIO_Pin_1, GPIO_ModeOut_PP_5mA ); } 在模拟方式下,其发送字节数据的函数为: void BH1750_SendByte(char data) { char i; IIC_OUTPUT_MODE_SET(); SCL_Clr(); DelayUs(2); for (i=0;i<8;i++) { if(data&0x80) { SDA_Set(); } else { SDA_Clr(); } data <<= 1; SCL_Set(); DelayUs(2); SCL_Clr(); DelayUs(2); } } 相应的字节数据接收函数为: char BH1750_RecvByte() { char i; char data = 0; IIC_INPUT_MODE_SET(); for (i=0;i<8;i++) { SCL_Clr(); DelayUs(2); SCL_Set(); data <<= 1; SCL_Set(); if(IIC_SDA_IN) data|=0x01; DelayUs(2); } SCL_Clr(); return data; } 有了以上函数的支持,实现光照强度采集的函数为: void Get_Sunlight_Value() { int dis_data=0; float temp; char i=0; unsigned int sd; Single_Write_BH1750(0x01); Single_Write_BH1750(0x10); DelayMs(180); Multiple_Read_BH1750(); for(i=0;i<3;i++) dis_data=BUF[0]; dis_data=(dis_data<<8)+BUF[1]; temp=(float)dis_data/1.2; sd=temp; PRINT(\"%d lx \\\\r\\\\n\", sd); } 在未添加显示器件的情况下,是通过串口来输出光照强度检测值。 其测试光照强度检测的主程序为: int main() { uint8_t i = 0; HSECFG_Capacitance(HSECap_18p); SetSysClock(CLK_SOURCE_HSE_PLL_62_4MHz); GPIOA_SetBits(GPIO_Pin_14); GPIOPinRemap(ENABLE, RB_PIN_UART0); GPIOA_ModeCfg(GPIO_Pin_15, GPIO_ModeIN_PU); GPIOA_ModeCfg(GPIO_Pin_14, GPIO_ModeOut_PP_5mA); UART0_DefInit(); PRINT(\"BH1750 test:\\\\r\\\\n\"); BH1750_Init(); while(1) { Get_Sunlight_Value(); DelayMs(500); } } 经程序的编译和下载,其检测情况如图2所示,说明其工作正常且有效。 图1 传感器连接 图2 检测结果
2025-07-06 00:08
开发环境: IDE:MounRiver Studio MCU:CH585 1 PWM输出的工作原理 脉冲宽度调制(PWM),是英文“Pulse Width Modulation” 的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制。 CH585的定时器都可以用来产生 PWM 输出。系统还提供了 8 路 8 位 PWM 输出(PWM4~PWM11)或6 路 16 位 PWM 输出(PWM4~PWM9),占空比可调,PWM 周期固定可选 8 种周期 2 PWM输出实现 2.1 定时器实现PWM 2.1.1 代码分析 本章要实现通过CH585实现PWM方波的输出,这里就需要将相应的GPIO设置为输出。定时器的引脚配置如下表所示。 Table ‑ 定时器 x GPIO的引脚映射如下表所示。 Table ‑ GPIO功能引脚映射 这里以TMR3为例介绍通过库函数来配置该功能的步骤。 首先要提到的是,PWM 相关的函数设置在库函数文件ch58x_timx.h 和ch58x_timx.c文件中。 1) 配置PB22为复用输出,用于PWM输出。 GPIOB_ResetBits(GPIO_Pin_22); // 配置PWM口 PB22 GPIOB_ModeCfg(GPIO_Pin_22, GPIO_ModeOut_PP_5mA); 2) 初始化 TMR3。 TMR3_PWMInit(High_Level, PWM_Times_1); 清空控制寄存器,设置PWM输出模式。有效重复次数为1次。 3) 设置 TMR3的 PWM 的周期和占空比。 笔者这里配置周期为100us,占空比为50%。 TMR3_PWMCycleCfg(g_10us * 10); // 周期 100us最大67108864 TMR3_PWMActDataWidth(g_10us/2 * 10);// 占空比 50%, 修改占空比必须暂时关闭定时器 4) 使能 TMR3。 最后需要使能PWM输出,启动TMR3 TMR3_PWMEnable(); TMR3_Enable(); 最后看下主函数代码: #include \"CH58x_common.h\" #defineg_10us(FREQ_SYS/100000) /********************************************************************* * @fnmain * * @Brief主函数 * * @Returnnone */ int main() { HSECFG_Capacitance(HSECap_18p); // 配置系统时钟(使用内部32MHz时钟) SetSysClock(CLK_SOURCE_HSE_PLL_62_4MHz); //PWM0 GPIOA_ResetBits(GPIO_Pin_9); // 配置PWM口 PA9 GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); TMR0_PWMInit(High_Level, PWM_Times_1); TMR0_PWMCycleCfg(g_10us * 10); // 周期 100us最大67108864 TMR0_PWMActDataWidth(g_10us/4 * 10);// 占空比 25%, 修改占空比必须暂时关闭定时器 TMR0_PWMEnable(); TMR0_Enable(); //PWM1 GPIOA_ResetBits(GPIO_Pin_10); // 配置PWM口 PA10 GPIOA_ModeCfg(GPIO_Pin_10, GPIO_ModeOut_PP_5mA); TMR1_PWMInit(High_Level, PWM_Times_1); TMR1_PWMCycleCfg(g_10us * 10); // 周期 100us最大67108864 TMR1_PWMActDataWidth(g_10us/3 * 10);// 占空比 33.3%, 修改占空比必须暂时关闭定时器 TMR1_PWMEnable(); TMR1_Enable(); //PWM2 GPIOA_ResetBits(GPIO_Pin_11); // 配置PWM口 PA11 GPIOA_ModeCfg(GPIO_Pin_11, GPIO_ModeOut_PP_5mA); TMR2_PWMInit(High_Level, PWM_Times_1); TMR2_PWMCycleCfg(g_10us * 10); // 周期 100us最大67108864 TMR2_PWMActDataWidth(g_10us/2 * 10);// 占空比 50%, 修改占空比必须暂时关闭定时器 TMR2_PWMEnable(); TMR2_Enable(); //PWM3 GPIOB_ResetBits(GPIO_Pin_22); // 配置PWM口 PB22 GPIOB_ModeCfg(GPIO_Pin_22, GPIO_ModeOut_PP_5mA); TMR3_PWMInit(High_Level, PWM_Times_1); TMR3_PWMCycleCfg(g_10us * 10); // 周期 100us最大67108864 TMR3_PWMActDataWidth(g_10us/4 * 10 * 3);// 占空比 75%, 修改占空比必须暂时关闭定时器 TMR3_PWMEnable(); TMR3_Enable(); while(1) { } } 是不是很简单。 2.1.2 PWM周期、占空比分析 根据前面的参数配置,我们可以算出PWM的输出周期: PWM频率 = 系统时钟频率 / PWM周期值 例如: 系统时钟:60MHz PWM周期值:10000 PWM频率 = 60,000,000 / 10,000 = 6kHz 占空比 = (比较值 / PWM周期值) × 100% 例如: 周期值:10000 比较值:3000 占空比 = 30% 2.1.3 PWM输出的实验现象 在前面我们输出了的PWM0(PA9)、PWM1(PA10)、PWM2(PA11)、PWM3(PB22)不同占空比的 PWM 信号。接下来就看看PWM的输出,PWM 信号可以通过示波器看到。下面笔者就是用逻辑分析仪查看波形。 首先笔者使用的逻辑分析仪是Kingst LA5016,当然啦,其他的也可以,关于逻辑分析仪的相关使用笔者这里就不介绍了,可以查看官方资料。 首先将(PA9)、(PA10)、(PA11)、(PB22)分别接到逻辑分析仪的CH0 – CH3,然后下载程序到板子中,打开Kingst VIS,然后进行采样。 我们就可以看到不同通道的实际周期,占空比等信息。从上图可以看到,实际测量的频率和占空比和理论是相符的。 2.2 PWM模块输出PWM 除了使用定时器产生的PWM,系统还提供了 8 路 8 位 PWM 输出(PWM4~PWM11)或6 路 16 位 PWM 输出(PWM4~PWM9)。 PWM 模块配置PWM的流程如下。 (1)、设置寄存器 R8_PWM_CLOCK_DIV,配置 PWM 的基准时钟频率; (2)、设置 PWM 输出极性配置寄存器 R8_PWM_POLAR,配置对应 PWMx 的输出极性; (3)、设置 PWM 配置控制寄存器 R8_PWM_CONFIG,设置 PWM 的模式、数据位宽、周期; (4)、设置 PWM 输出使能寄存器 R8_PWM_OUT_EN,开启对应的 PWMx 输出使能; (5)、根据需要的占空比计算出数据,写入对应的数据保持寄存器 R8_PWMx_DATA; (6)、设置 PWM4-PWM11 中所需的 PWM 引脚方向为输出,可选地,设置相应 I/O 的驱动能力; (7)、根据需要更新 R8_PWMx_DATA 中的数据,更新输出占空比。 2.2.1 代码实现 首先要提到的是,PWM模块相关的函数设置在库函数文件CH58x_pwm.h 和CH58x_pwm.c文件中。 1) 配置PB12为复用输出,用于PWM输出。 GPIOB_ModeCfg(GPIO_Pin_12, GPIO_ModeOut_PP_5mA); 2) 配置PWM模块的时钟和周期 PWMX_CLKCfg(4); PWMX_16bit_CycleCfg(60000-1); 3) 配置PWM模块的占空比 PWMX_16bit_ACTOUT(CH_PWM4, 30000, Low_Level, ENABLE);// 50%占空比 最后配置PWM模块占空比即可。 主函数代码如下。 #include \"CH58x_common.h\" #defineg_10us(FREQ_SYS/100000) /********************************************************************* * @fnmain * * @brief主函数 * * @returnnone */ int main() { HSECFG_Capacitance(HSECap_18p); // 配置系统时钟(使用内部32MHz时钟) SetSysClock(CLK_SOURCE_HSE_PLL_62_4MHz); /* 配置GPIO */ GPIOA_ModeCfg(GPIO_Pin_12, GPIO_ModeOut_PP_5mA); // PA12 - PWM4 GPIOA_ModeCfg(GPIO_Pin_13, GPIO_ModeOut_PP_5mA); // PA13 - PWM5 GPIOB_ModeCfg(GPIO_Pin_0, GPIO_ModeOut_PP_5mA);// PB0 - PWM6 GPIOB_ModeCfg(GPIO_Pin_4, GPIO_ModeOut_PP_5mA);// PB4 - PWM7 GPIOB_ModeCfg(GPIO_Pin_6, GPIO_ModeOut_PP_5mA);// PB6 - PWM8 GPIOB_ModeCfg(GPIO_Pin_7, GPIO_ModeOut_PP_5mA);// PB7 - PWM9 PWMX_CLKCfg(4);// cycle = 4/Fsys PWMX_16bit_CycleCfg(60000-1);// 16位数据宽度Ncyc=RB_PWM_CYC_VALUE+1 PWMX_16bit_ACTOUT(CH_PWM4, 30000, Low_Level, ENABLE);// 50%占空比 PWMX_16bit_ACTOUT(CH_PWM5, 15000, Low_Level, ENABLE);// 25%占空比 PWMX_16bit_ACTOUT(CH_PWM6, 45000, Low_Level, ENABLE);// 75%占空比 PWMX_16bit_ACTOUT(CH_PWM7, 30000, High_Level, ENABLE); // 50%占空比 PWMX_16bit_ACTOUT(CH_PWM8, 15000, High_Level, ENABLE); // 25%占空比 PWMX_16bit_ACTOUT(CH_PWM9, 45000, High_Level, ENABLE); // 75%占空比 while(1) { } } 2.2.2 PWM输出的实验现象 在前面我们输出了的PWM4(PA12)、PWM5(PA13)、PWM6(PB0)、PWM7(PB4) 、PWM8(PB6) 、PWM9(PB7)不同占空比的 PWM 信号。接下来就看看PWM的输出,PWM 信号可以通过示波器看到。下面笔者就是用逻辑分析仪查看波形。 首先笔者使用的逻辑分析仪是Kingst LA5016,当然啦,其他的也可以,关于逻辑分析仪的相关使用笔者这里就不介绍了,可以查看官方资料。 首先将PWM4(PA12)、PWM5(PA13)、PWM6(PB0)、PWM7(PB4) 、PWM8(PB6) 、PWM9(PB7)分别接到逻辑分析仪的CH0 – CH5,然后下载程序到板子中,打开Kingst VIS,然后进行采样。 我们就可以看到不同通道的实际周期,占空比等信息。从上图可以看到,实际测量的频率和占空比和理论是相符的。
2025-07-05 23:16