GD32 Timer+ADC多通道+DMA+PWM调试记录

本例记录使用GD32307C开发板,实现以内部Timer1 CH1为触发源,触发ADC0的两个通道,进行并行非连续采样,病通过DMA传输采样结果。同时输出PWM,用来检测Timer1 CH1的触发周期。

下面介绍具体实现过程:

1. gpio初始化

本例需要用到以下三根IO:

PA1 -- TIMER1_1,PWM
PC3 -- ADC0_CH13
PC5 -- ADC0_CH15

查询芯片手册需要将PC3/PC5设定为模拟输入AIN(ADC功能),将PA1设定为备份功能输出脚AF_PP(PWM输出)。

以PA1为例,设定为Alternate功能后,如果有启动Timer1,则该脚位的功能为TIMER1_CH1。

  1. 使能GPIO Group时钟。
  2. 使能Alternate Function时钟。
  3. GPIO 功能config 。

代码如下:

/*!\brief      configure the GPIO peripheral\param[in]  none\param[out] none\retval     none
*/
//PA1 -- TIMER1_1,PWM
//PC3 -- ADC0_CH13
//PC5 -- ADC0_CH15
static void gpio_config(void)
{/* enable GPIOC clock */rcu_periph_clock_enable(RCU_GPIOC);rcu_periph_clock_enable(RCU_GPIOA); /* 开启复用功能时钟 */rcu_periph_clock_enable(RCU_AF);	/* config the GPIO as analog mode */  gpio_init(GPIOC, GPIO_MODE_AIN, GPIO_OSPEED_MAX, GPIO_PIN_3|GPIO_PIN_5);/*configure PB3(TIMER1 CH1) as alternate function*/gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
}

2.Timer初始化

这里配置Timer1的定时周期为1ms,附上Timer时间公式:

定时时间:Time= (1+prescaler)/ systemcoreclock *(1+period)

我这边systemcoreclock为120MHZ。

设定CH1位PWM0模式输出,用来验证ADC采样间隔(ADC采样以内部Timer1_CH1为触发源)。

在下面代码里面,我们设定为边沿对齐方式(EAPWM),向上计数:

EAPWM的周期由TIMERx_CAR寄存器值决定,占空比由TIMERx_CHxCV寄存器值决定。

这里介绍一下PWM的工作模式(PWM0/PWM1)

PWM的工作模式:

  • PWM 模式0。在向上计数时,一旦计数器值小于TIMERx_CH0CV时,
    O0CPRE为有效电平,否则为无效电平。在向下计数时,一旦计数器的值大
    于TIMERx_CH0CV时,O0CPRE 为无效电平,否则为有效电平。
  • PWM 模式1。在向上计数时,一旦计数器值小于TIMERx_CH0CV时,
    O0CPRE为无效电平,否则为有效电平。在向下计数时,一旦计数器的值大
    于TIMERx_CH0CV时,O0CPRE为有效电平,否则为无效电平。

占空比计算公式:

Duty = A/(N+1)   

N = Timer装载值

A = TIMER_CHxCV,即timer_channel_output_pulse_value_config的最后一个参数

EAPWM的时序示意图如下:

代码如下:

/*!\brief      configure the timer peripheral\param[in]  none\param[out] none\retval     none
*/
static void timer1_config(void)	// 1 ms
{timer_oc_parameter_struct timer_ocintpara;timer_parameter_struct timer_initpara;//使能定时器时钟rcu_periph_clock_enable(RCU_TIMER1);timer_deinit(TIMER1);/*  TIMER1 基本配置 */timer_initpara.prescaler				 = 119;timer_initpara.alignedmode			 = TIMER_COUNTER_EDGE;timer_initpara.counterdirection  = TIMER_COUNTER_UP;timer_initpara.period 					 = 999;timer_initpara.clockdivision		 = TIMER_CKDIV_DIV1;timer_initpara.repetitioncounter = 0;timer_init(TIMER1,&timer_initpara);/* CH1 configuration in PWM mode1 */timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;		/* 通道使能 */    timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;		/* 通道互补输出使能(定时器2无效) */    timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;	/* 通道极性 */    timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;/* 互补通道极性(定时器2无效)*/    timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;/* 通道空闲状态输出(定时器2无效)*/    timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;/*互补通道空闲状态输出(定时器2无效) */timer_channel_output_config(TIMER1, TIMER_CH_1, &timer_ocintpara);/* duty 占空比设定*/timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_1, 500);timer_channel_output_mode_config(TIMER1, TIMER_CH_1, TIMER_OC_MODE_PWM0);timer_channel_output_shadow_config(TIMER1, TIMER_CH_1, TIMER_OC_SHADOW_DISABLE);timer_auto_reload_shadow_enable(TIMER1);
}

使用示波器量测PA1脚位,输出为1KHZ的方波。

3. dma初始化

这里我们选择DMA0_CH0,这里我们设定为16bit宽度数据源,与16bit外设数据宽度。DMA传输数量设定为100.

所以我们定义一个100 uint16_t的数组作为DMA存放数据的内存。

开启半满与全满中断。

代码如下:

/*!\brief      configure the DMA peripheral\param[in]  none\param[out] none\retval     none
*/
uint16_t adc_value[100];
uint32_t dmaHalfIntCnt; static void dma_config(void)
{/* ADC_DMA_channel configuration */dma_parameter_struct dma_data_parameter;/* enable DMA clock */rcu_periph_clock_enable(RCU_DMA0);/* 复位dma 通道0  */dma_deinit(DMA0, DMA_CH0);/* initialize DMA data mode */dma_data_parameter.periph_addr	= (uint32_t)(&ADC_RDATA(ADC0));//配置外设寄存器为adc的规则数据寄存器dma_data_parameter.periph_inc 	= DMA_PERIPH_INCREASE_DISABLE;//外设寄存器地址不增加dma_data_parameter.memory_addr	= (uint32_t)(&adc_value);//存放数据的内存地址dma_data_parameter.memory_inc 	= DMA_MEMORY_INCREASE_ENABLE;//存放数据的内存地址自增dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;//外设数据宽度32位 adc1[16-31]adc0[0-15]dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT;  //内存数据宽度32位dma_data_parameter.direction		= DMA_PERIPHERAL_TO_MEMORY;//数据传输方式是外设到内存dma_data_parameter.number 			= 100;//dma传输数量dma_data_parameter.priority 		= DMA_PRIORITY_HIGH;//dma优先级高dma_init(DMA0, DMA_CH0, &dma_data_parameter);//循环模式开启dma_circulation_enable(DMA0, DMA_CH0);/* dam 转换结束产生中断*/dma_interrupt_enable(DMA0, DMA_CH0, DMA_INT_FTF);		dma_interrupt_enable(DMA0, DMA_CH0, DMA_INT_HTF);	/* 使能dma通道0*/dma_channel_enable(DMA0, DMA_CH0);//中断管理器开启通道4中断nvic_irq_enable(DMA0_Channel0_IRQn, 0, 0);	
}

DMA中断程序如下:(函数名与startup中的中断向量表函数名一致)

void DMA1_Channel1_IRQHandler(void)
{if(dma_interrupt_flag_get(DMA0, DMA_CH0, DMA_INT_FLAG_FTF)){  //清除dma中断标记dma_interrupt_flag_clear(DMA0, DMA_CH0, DMA_INT_FLAG_FTF);gd_eval_led_toggle(LED2);//dma_finish = SET;} else if(dma_interrupt_flag_get(DMA0, DMA_CH0, DMA_INT_FLAG_HTF)){//清除dma中断标记dma_interrupt_flag_clear(DMA0, DMA_CH0, DMA_INT_FLAG_HTF);dmaHalfIntCnt ++;}
}

截取部分Startup_xx.s向量表

g_pfnVectors:

  .
.word EXTI4_IRQHandler
.word DMA1_Channel1_IRQHandler
.

4. ADC初始化

我们使用ADC0的CH13/CH15两个通道,这里设置ADC为规则并行模式。

开启扫描模式,ADC会自动采样转换开启的通道。

不要开启连续扫描,采用间断扫描方式。

设定TIM1_CH1为外部触发源,病使能ADC的DMA mode,具体代码如下:

/*!\brief      configure the ADC peripheral\param[in]  none\param[out] none\retval     none
*/
static void adc_config(void)
{/* enable ADC0 clock */rcu_periph_clock_enable(RCU_ADC0);/* reset ADC */adc_deinit(ADC0);/* ADC mode config 工作在规则并行模式 *///adc_mode_config(ADC_MODE_FREE); adc_mode_config(ADC_DAUL_REGULAL_PARALLEL); // 连续转换模式使能//adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE);/* ADC continous function enable *///adc_special_function_config(ADC0, ADC_SCAN_MODE, DISABLE); adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE); /* 右对齐*/adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);/* 配置规则通道数量 *///adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1);adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 2);/* ADC规则模式配置;2个adc并行模式下,不可以2个adc同时采集一个通道,adc1数据放在高16位,adc0数据放在低16位*/adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_13, ADC_SAMPLETIME_55POINT5);adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_15, ADC_SAMPLETIME_55POINT5);/* adc 使用TIM1 的CH1 的上升沿触发*/adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_EXTTRIG_REGULAR_T1_CH1);/* adc触发使能*/adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);/* 使能ADC*/adc_enable(ADC0);delay_1ms(1U);/* 标定复位ADC */adc_calibration_enable(ADC0);/* ADC DMA 使能*/adc_dma_mode_enable(ADC0);
}

5. 测试代码

这里创建一个thread来测试,测试代码如下:

void LED1_Task(void* parameter)
{gpio_config();timer1_config();dma_config();adc_config();timer_enable(TIMER1);while(1){//gd_eval_led_toggle(LED3);printf("%s tick:%d \n",__FUNCTION__,xTaskGetTickCount());		//Ben 221024#1printf("dmaHalfIntCnt:%d \n",dmaHalfIntCnt);printf("%d %d %d %d\n",adc_value[0],adc_value[1],adc_value[2],adc_value[3]);		vTaskDelay(500); }
}

在500ms间隔下的测试结果为:

dmaHalfIntCnt:2410 
1663 0 1663 1
LED1_Task tick:120965 
dmaHalfIntCnt:2420 
1663 1 1663 0
LED1_Task tick:121469 
dmaHalfIntCnt:2430 
1663 0 1661 1
LED1_Task tick:121973 
dmaHalfIntCnt:2440 
1663 0 1663 0
LED1_Task tick:122477 
dmaHalfIntCnt:2450 

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

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

相关文章

阻塞 IO为什么叫BIO,非阻塞IO为什么叫NIO,异步IO为什么叫AIO

IOIO的核心就是数据传输,也就是程序与外部设备之间进行传输,通过IO的核心可以分为,文件IO和网络IO文件IO交互的对象就是本地存储设备,比方说读写本地文件。网络IO交互的对象就是网络设备,核心的应用场景就是网络通信。…

10分钟了解什么是多模态大模型

10分钟了解什么是多模态大模型(MM-LLMs) 1. 什么是多模态 Multimodality 多模态(Multimodality)是指集成和处理两种或两种以上不同类型的信息或数据的方法和技术。在机器学习和人工智能领域,多模态涉及的数据类型通常…

通过DSL生成Jenkins流水线

代码化管理 Jenkins 流水线(Infrastructure as Code) 版本控制:DSL 脚本可以像代码一样存入 Git、GitLab 等版本控制系统,所有任务配置的变更都有提交记录,便于追溯历史、回滚错误。协作效率:团队成员可以通…

信号量主要API及综合应用

1.信号量概述信号量是一个底层核心模块【int】类型变量,记录当前信号量数据。信号量 P 操作 (sem_wait)线程检测对应信号量底层 int 数据数值,如果大于 0,当前线程获得 CPU 执行权,同时将信号量底层 int 数据-1 操作。如果底层数据…

工业自动化领域的“超级跑车”:西门子TDC系统深度解析与实战架构

工业自动化领域的“超级跑车”:西门子TDC系统深度解析与实战架构 文章目录 工业自动化领域的“超级跑车”:西门子TDC系统深度解析与实战架构引言:当普通PLC遇到性能瓶颈第一章:认识TDC——它不是简单的“大型PLC”1.1 TDC究竟是什…

MySQL高阶查询语句与视图实战指南

MySQL高阶查询语句与视图实战指南 文章目录MySQL高阶查询语句与视图实战指南一、常用高阶查询技巧1. 按关键字排序(ORDER BY)基础用法进阶用法:多字段排序条件过滤2. 区间判断与去重(AND/OR DISTINCT)区间判断&#x…

解决Pytest参数化测试中文显示乱码问题:两种高效方法

在使用Pytest进行参数化测试时,许多开发者都会遇到一个常见但令人头疼的问题:当测试用例的ids参数包含中文字符时,控制台输出会出现乱码。这不仅影响了测试报告的可读性,也给测试结果的分析带来了困难。本文将深入探讨这个问题&am…

基于SpringBoot的校园流浪动物救助平台【spring boot实战项目、Java毕设、Java项目、Java实战】

💖💖作者:计算机毕业设计小途 💙💙个人简介:曾长期从事计算机专业培训教学,本人也热爱上课教学,语言擅长Java、微信小程序、Python、Golang、安卓Android等,开发项目包括…

利用kimi k2编写postgresql协议服务端的尝试

美团龙猫还是很有自知之明的 提问请用C编写postgresql协议服务端,能接收psql客户端或其他采用postgresql协议的工具的请求,实现将用户请求打印在控制台,并把回应发给客户端回答 抱歉,我无法为您编写完整的 PostgreSQL 协议服务端。…

医疗 AI 再突破:辅助诊断准确率超 90%,但落地医院仍面临数据安全与临床信任难题

一、引言(一)医疗 AI 发展背景在数字化与智能化浪潮的席卷下,医疗领域正经历着深刻变革,人工智能(AI)技术的融入成为这场变革的关键驱动力。近年来,医疗 AI 辅助诊断技术取得重大突破&#xff0…

Rocky Linux10.0安装zabbix7.4详细步骤

安装Rocky Linux10.0系统 请参考Rocky Linux10.0安装教程-CSDN博客 查看当前系统版本 cat /etc/*release 安装数据库 安装zabbix之前,需要先安装一个数据库来承载zabbix的数据。这里我选择在本机直接安装一个MariaDB数据库。 Rocky Linux10.0系统默认不包含MySQ…

JDBC插入数据

文章目录视频:JDBC插入数据环境准备写插入数据属性配置属性配置视频:JDBC插入数据 环境准备 MySQL环境 小皮面板 提供MySQL环境 写插入数据 属性配置 声明变量 属性配置 # . properties 是一个特俗的map 集合 # key : 字符串 value : 字符串…

GPU 服务器压力测试核心工具全解析:gpu-burn、cpu-burn 与 CUDA Samples

在 GPU 服务器的性能验证、稳定性排查与运维管理中,压力测试是关键环节,可有效检测硬件极限性能、散热效率及潜在故障。以下从工具原理、核心功能、使用场景等维度,详细介绍三款核心测试工具,帮助用户系统掌握 GPU 服务器压力测试方法。 一、GPU 专属压力测试工具:gpu-bu…

Python进程和线程——多线程

前面提到过进程是由很多线程组成的,那么今天廖老师就详细解释了线程是如何运行的。首先,,Python的标准库提供了两个模块:_thread和threading,_thread是低级模块,threading是高级模块,对_thread进…

【MySQL|第九篇】视图、函数与优化

目录 十、视图 1、简单视图: 2、复杂视图: 3、视图更新: 十一、函数 1、函数创建: 十二、数据库优化 1、索引优化: 2、查询优化: 3、设计优化: 十、视图 在 MySQL 中,视图…

使用Docker和虚拟IP在一台服务器上灵活部署多个Neo4j实例

使用Docker和虚拟IP在一台服务器上灵活部署多个Neo4j实例 前言 在现代应用开发中,图数据库Neo4j因其强大的关系处理能力而备受青睐。但有时候我们需要在同一台服务器上运行多个Neo4j实例,比如用于开发测试、多租户环境或者A/B测试。传统的端口映射方式…

K8s学习笔记(一):Kubernetes架构-原理-组件

Kubernetes(简称 K8s)是一款开源的容器编排平台,核心目标是实现容器化应用的自动化部署、扩展、故障恢复和运维管理。其设计遵循 “主从架构”(Control Plane Node),组件分工明确,通过 “声明式…

ensp配置学习笔记 比赛版 vlan 静态路由 ospf bgp dhcp

学习配置VLAN 虚拟局域网,目的让两台在同一网段的设备,在交换机中访问。基础指令:sys 进入系统 sysname R1 修改交换机名字为R1 display cur 查看数据、端口等交换机信息 (在端口中,可以直接display this 可以直接看…

仓颉编程语言青少年基础教程:enum(枚举)类型和Option类型

仓颉编程语言青少年基础教程:enum(枚举)类型和Option类型enum 和 Option 各自解决一类“语义级”问题:enum 让“取值只在有限集合内”的约束从注释变成编译器强制;Option 让“值可能不存在”的语义显式化。enum类型enu…

javaEE-Spring IOCDI

目录 1、什么是Spring: 2.什么是IoC: 3. 什么是控制反转呢? 4.IoC容器具备以下优点: 5.DI是什么: 依赖注⼊方法: 三种注入方法的优缺点: Autowired注解注入存在的问题: Autowired和Resource的区别&#xff…