- 2025-04-15
-
回复了主题帖:
[航芯高性能MCU系列:ACM32F403开发板]4.梯形加减速实现对滑台的运动控制
秦天qintian0303 发表于 2025-4-15 11:36
加速和减速过程是恒定的还是可以根据最大行程变化?
变化的
-
回复了主题帖:
航芯ACM32F403怎么进行调试啊
之前到官网下载的只有KEIL的PACK包,没有IAR的;如果IAR本身不支持这个型号,那就不要用IAR开发环境;改用KEIL开发环境就可以了……
- 2025-04-14
-
发表了主题帖:
[航芯高性能MCU系列:ACM32F403开发板]4.梯形加减速实现对滑台的运动控制
本帖最后由 xld0932 于 2025-4-14 21:12 编辑
1.前言
通过学习正点原子实现步进电机进行梯形加减速控制,实现对滑台的运动控制
2.梯形加减速简介(《摘录于DMF407电机控制专题教程》)
步进电机因其无需反馈就能对位置和速度进行控制而在工业自动化设备中的应用极为广泛,如下图所示,假设该装置使用步进电机实现物体 X 的移动,系统要求从 A 点出发,到 B点停止,移动的时间越短越好且系统稳定。
根据步进电机的特性,最大程度加大电机转速(提高脉冲频率),则到达 B 点的时间就越短,但是如果施加脉冲频率太高, 超过了步进电机的最小启动频率,则会造成电机内部的反向电动势的阻尼作用,转子与定子之间的磁反应将跟随不上电信号的变化,导致堵转或者丢步,滑块连动都没动,非常尴尬!
所以要求在电机启动时需要一个低速,但为了实现系统要求,在启动之后慢慢的升高速度,实现一个加速的过程,那如果在达到终点前一直在加速,就又有另外一个问题,到达终点时速度太快就会导致刹不住,出现过冲现象,为了防止这个现象我们需要在达到终点前的一段距离提前减速,使得滑块到达终点时速度刚好为 0,即从最高速度一直减至停止。如果在加速的过程中,从启动速度到目标速度的加减速是以一定的比例进行加速/减速,则可以将这个过程描述为如下图所示:
由上图可知,整个运动过程将滑块的速度变化一共分为三个阶段, OA 段其实就是滑块的加速部分、 AB 则是匀速部分, BC 则是减速部分。因其图像呈现梯形,所以我们把这种运动变化称之为梯形加减速。
因为要实现加减速效果,所以输送给电机的脉冲的周期是变化的,只要确定下一个脉冲的周期,并且确定加速阶段和减速阶段需要提供多少个脉冲,然后让控制器一个一个把脉冲送出去即可。如下图所示:
第一种设置情况:最大速度设置偏大,而加速度设置偏小,导致电机步进了很多步数依然没有达到最大速度,但此时距离终点 Step 可走的步速越来越少,再不减速将无法在终点时使速度降为 0。所以这种情况,其加速模型并不呈现梯形,加速不到最大速度将要被迫降速。
第二种设置情况:最大速度设置偏小,而加速度设置偏大,步进电机很快就加速到了设置的最大速度,距离设置的终点 max_w 还有剩余很长的步数可以走,此时,电机以最大速度匀速步进,直到满足减速条件才开始减速,并刚好使得速度到终点时降至 0。
梯形加减速整体流程:
梯形加减速整体流程各状态切换:
3.梯形加减速的控制实现
3.1.电机相关参数及宏定义
enum STEPPER_STATE
{
STOP = 0, /* 加减速曲线状态:停止*/
ACCEL = 1, /* 加减速曲线状态:加速阶段*/
DECEL = 2, /* 加减速曲线状态:减速阶段*/
RUN = 3, /* 加减速曲线状态:匀速阶段*/
};
enum STEPPER_DIRECTION
{
CCW = 0, /* 逆时针 */
CW = 1, /* 顺时针 */
};
enum STEPPER_EN
{
EN_ON = 0, /* 失能脱机引脚 */
EN_OFF = 1, /* 使能脱机引脚 使能后电机停止旋转 */
};
typedef struct
{
__IO uint8_t run_state; /* 电机旋转状态 */
__IO uint8_t direction; /* 电机旋转方向 */
__IO int32_t step_delay; /* 下个脉冲周期(时间间隔),启动时为加速度 */
__IO uint32_t decel_start; /* 开始减速位置 */
__IO int32_t decel_value; /* 减速阶段步数 */
__IO int32_t min_delay; /* 速度最快,计数值最小的值(最大速度,即匀速段速度) */
__IO int32_t accel_count; /* 加减速阶段计数值 */
} speedRampData;
#define MICRO_STEP 8 /* 步进电机驱动器细分数 */
#define TIM_FREQ 180000000U /* 定时器主频 */
#define MAX_STEP_ANGLE 0.1125 /* 最小步距(0.9/MICRO_STEP) */
#define PAI 3.1415926 /* 圆周率*/
#define FSPR 200 /* 步进电机单圈步数 */
#define T1_FREQ (TIM_FREQ/80) /* 频率ft值 */
#define SPR (FSPR*MICRO_STEP) /* 旋转一圈需要的脉冲数 */
/* 数学常数 */
#define ALPHA ((float)(2*PAI/SPR)) /* α = 2*pi/spr */
#define A_T_x10 ((float)(10*ALPHA*T1_FREQ))
#define T1_FREQ_148 ((float)((T1_FREQ*0.69)/10)) /* 0.69为误差修正值 */
#define A_SQ ((float)(2*100000*ALPHA))
#define A_x200 ((float)(200*ALPHA)) /* 2*10*10*a/10 */
3.3.控制引脚及定时器初始化
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload)
{
uint16_t tmpccmr1 = 0;
tmpccmr1 = TIMx->CCMR1;
tmpccmr1 &= (uint16_t)~((uint16_t)0x0008);
tmpccmr1 |= TIM_OCPreload;
TIMx->CCMR1 = tmpccmr1;
}
void stepper_init(uint16_t arr, uint16_t psc)
{
GPIO_InitTypeDef GPIO_InitStruct;
System_Module_Enable(EN_GPIOCD);
/* Direction */
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = GPIO_FUNCTION_0;
GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_WriteBit(GPIOC, GPIO_PIN_9, Bit_SET);
/* Limit Switch */
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10 | GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = GPIO_FUNCTION_0;
GPIO_Init(GPIOC, &GPIO_InitStruct);
TIM_Base_InitTypeDef TIM_Base_InitStruct;
TIM_OC_InitTypeDef TIM_OC_InitStruct;
uint32_t TIM_Clock;
System_Module_Enable(EN_GPIOCD);
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Alternate = GPIO_FUNCTION_2;
GPIO_Init(GPIOC, &GPIO_InitStruct);
System_Module_Enable(EN_TIM1);
if (System_Get_SystemClock() == System_Get_APBClock())
{
TIM_Clock = System_Get_APBClock();
}
else
{
TIM_Clock = System_Get_APBClock() * 2;
}
TIM_Base_InitStruct.Prescaler = psc;
TIM_Base_InitStruct.Period = arr;
TIM_Base_InitStruct.RepetitionCounter = 0;
TIM_Base_InitStruct.CounterMode = TIM_COUNTERMODE_UP;
TIM_Base_InitStruct.ClockDivision = TIM_CLOCKDIVISION_DIV1;
TIM_TimeBase_Init(TIM1, &TIM_Base_InitStruct);
TIM_OC_InitStruct.OCMode = OUTPUT_MODE_MATCH_TOGGLE;
TIM_OC_InitStruct.Pulse = 0;
TIM_OC_InitStruct.OCPolarity = OUTPUT_POL_ACTIVE_HIGH;
TIM_OC_InitStruct.OCNPolarity = OUTPUT_POL_ACTIVE_HIGH;
TIM_OC_InitStruct.OCFastMode = OUTPUT_FAST_MODE_DISABLE;
TIM_OC_InitStruct.OCIdleState = OUTPUT_IDLE_STATE_0;
TIM_OC_InitStruct.OCNIdleState = OUTPUT_IDLE_STATE_0;
TIM_OC1Init(TIM1, &TIM_OC_InitStruct);
TIM_OC1PreloadConfig(TIM1, 0);
TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE);
TIM_ClearFlag(TIM1, TIM_IT_CC1);
NVIC_ClearPendingIRQ(TIM1_CC_IRQn);
NVIC_EnableIRQ(TIM1_CC_IRQn);
TIM_Cmd(TIM1, ENABLE);
TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
3.3.生成梯形运行控制参数
void create_t_ctrl_param(int32_t step, uint32_t accel, uint32_t decel, uint32_t speed)
{
volatile uint16_t tim_count; /* 达到最大速度时的步数*/
volatile uint32_t max_s_lim; /* 必须要开始减速的步数(如果加速没有达到最大速度)*/
volatile uint32_t accel_lim;
if (g_motion_state != STOP) /* 只允许步进电机在停止的时候才继续*/
{
return;
}
if (step < 0) /* 步数为负数 */
{
g_srd.direction = CCW; /* 逆时针方向旋转 */
GPIO_WriteBit(GPIOC, GPIO_PIN_9, Bit_SET);
step = -step; /* 获取步数绝对值 */
}
else
{
g_srd.direction = CW; /* 顺时针方向旋转 */
GPIO_WriteBit(GPIOC, GPIO_PIN_9, Bit_RESET);
}
if (step == 1) /* 步数为1 */
{
g_srd.accel_count = -1; /* 只移动一步 */
g_srd.run_state = DECEL; /* 减速状态 */
g_srd.step_delay = 1000; /* 默认速度 */
}
else if (step != 0) /* 如果目标运动步数不为0*/
{
/*设置最大速度极限, 计算得到min_delay用于定时器的计数器的值 min_delay = (alpha / t)/ w*/
g_srd.min_delay = (int32_t)(A_T_x10 / speed); //匀速运行时的计数值
/*
* 通过计算第一个(c0) 的步进延时来设定加速度,其中accel单位为0.1rad/sec^2
* step_delay = 1/tt * sqrt(2*alpha/accel)
* step_delay = ( tfreq*0.69/10 )*10 * sqrt( (2*alpha*100000) / (accel*10) )/100
*/
g_srd.step_delay = (int32_t)((T1_FREQ_148 * sqrt(A_SQ / accel)) / 10); /* c0 */
max_s_lim = (uint32_t)(speed * speed / (A_x200 * accel / 10)); /* 计算多少步之后达到最大速度的限制 max_s_lim = speed^2 / (2*alpha*accel) */
if (max_s_lim == 0) /* 如果达到最大速度小于0.5步,我们将四舍五入为0,但实际我们必须移动至少一步才能达到想要的速度 */
{
max_s_lim = 1;
}
accel_lim = (uint32_t)(step * decel / (accel + decel)); /* 这里不限制最大速度 计算多少步之后我们必须开始减速 n1 = (n1+n2)decel / (accel + decel) */
if (accel_lim == 0) /* 不足一步 按一步处理*/
{
accel_lim = 1;
}
if (accel_lim <= max_s_lim) /* 加速阶段到不了最大速度就得减速。。。使用限制条件我们可以计算出减速阶段步数 */
{
g_srd.decel_value = accel_lim - step; /* 减速段的步数 */
}
else
{
g_srd.decel_value = -(max_s_lim * accel / decel); /* 减速段的步数 */
}
if (g_srd.decel_value == 0) /* 不足一步 按一步处理 */
{
g_srd.decel_value = -1;
}
g_srd.decel_start = step + g_srd.decel_value; /* 计算开始减速时的步数 */
if (g_srd.step_delay <= g_srd.min_delay) /* 如果一开始c0的速度比匀速段速度还大,就不需要进行加速运动,直接进入匀速 */
{
g_srd.step_delay = g_srd.min_delay;
g_srd.run_state = RUN;
}
else
{
g_srd.run_state = ACCEL;
}
g_srd.accel_count = 0; /* 复位加减速计数值 */
}
g_motion_state = 1; /* 电机为运动状态 */
tim_count = TIM1->CCR1;
TIM1->CCR1 = tim_count + g_srd.step_delay / 2; /* 设置定时器比较值 */
TIM_CCxCmd(TIM1, TIM_CHANNEL_1, TIM_CCx_Enable); /* 使能定时器通道 */
}
3.4.定时器比较中断
void TIM1_CC_IRQHandler(void)
{
volatile uint32_t tim_count = 0;
volatile uint32_t tmp = 0;
volatile uint16_t new_step_delay = 0; /* 保存新(下)一个延时周期 */
volatile static uint16_t last_accel_delay = 0; /* 加速过程中最后一次延时(脉冲周期) */
volatile static uint32_t step_count = 0; /* 总移动步数计数器*/
volatile static int32_t rest = 0; /* 记录new_step_delay中的余数,提高下一步计算的精度 */
volatile static uint8_t i = 0; /* 定时器使用翻转模式,需要进入两次中断才输出一个完整脉冲 */
if (TIM_GetFlagStatus(TIM1, TIM_IT_CC1) != RESET)
{
TIM_ClearFlag(TIM1, TIM_IT_CC1);
tim_count = TIM1->CCR1;
tmp = tim_count + g_srd.step_delay / 2; /* 整个C值里边是需要翻转两次的所以需要除以2 */
TIM1->CCR1 = tmp;
i++; /* 定时器中断次数计数值 */
if (i == 2) /* 2次,说明已经输出一个完整脉冲 */
{
i = 0; /* 清零定时器中断次数计数值 */
switch (g_srd.run_state) /* 加减速曲线阶段 */
{
case STOP:
step_count = 0; /* 清零步数计数器 */
rest = 0; /* 清零余值 */
/* 关闭通道*/
TIM_CCxCmd(TIM1, TIM_CHANNEL_1, TIM_CCx_Disable);
g_motion_state = 0; /* 电机为停止状态 */
break;
case ACCEL:
g_add_pulse_count++; /* 只用于记录相对位置转动了多少度 */
step_count++; /* 步数加1*/
if (g_srd.direction == CW)
{
g_step_position++; /* 绝对位置加1 记录绝对位置转动多少度*/
}
else
{
g_step_position--; /* 绝对位置减1*/
}
g_srd.accel_count++; /* 加速计数值加1*/
new_step_delay = g_srd.step_delay - (((2 * g_srd.step_delay) + rest) / (4 * g_srd.accel_count + 1)); /* 计算新(下)一步脉冲周期(时间间隔) */
rest = ((2 * g_srd.step_delay) + rest) % (4 * g_srd.accel_count + 1); /* 计算余数,下次计算补上余数,减少误差 */
if (step_count >= g_srd.decel_start) /* 检查是否到了需要减速的步数 */
{
g_srd.accel_count = g_srd.decel_value; /* 加速计数值为减速阶段计数值的初始值 */
g_srd.run_state = DECEL; /* 下个脉冲进入减速阶段 */
}
else if (new_step_delay <= g_srd.min_delay) /* 检查是否到达期望的最大速度 计数值越小速度越快,当你的速度和最大速度相等或更快就进入匀速*/
{
last_accel_delay = new_step_delay; /* 保存加速过程中最后一次延时(脉冲周期)*/
new_step_delay = g_srd.min_delay; /* 使用min_delay(对应最大速度speed)*/
rest = 0; /* 清零余值 */
g_srd.run_state = RUN; /* 设置为匀速运行状态 */
}
break;
case RUN:
g_add_pulse_count++;
step_count++; /* 步数加1 */
if (g_srd.direction == CW)
{
g_step_position++; /* 绝对位置加1 */
}
else
{
g_step_position--; /* 绝对位置减1*/
}
new_step_delay = g_srd.min_delay; /* 使用min_delay(对应最大速度speed)*/
if (step_count >= g_srd.decel_start) /* 需要开始减速 */
{
g_srd.accel_count = g_srd.decel_value; /* 减速步数做为加速计数值 */
new_step_delay = last_accel_delay; /* 加阶段最后的延时做为减速阶段的起始延时(脉冲周期) */
g_srd.run_state = DECEL; /* 状态改变为减速 */
}
break;
case DECEL:
step_count++; /* 步数加1 */
g_add_pulse_count++;
if (g_srd.direction == CW)
{
g_step_position++; /* 绝对位置加1 */
}
else
{
g_step_position--; /* 绝对位置减1 */
}
g_srd.accel_count++;
new_step_delay = g_srd.step_delay - (((2 * g_srd.step_delay) + rest) / (4 * g_srd.accel_count + 1)); /* 计算新(下)一步脉冲周期(时间间隔) */
rest = ((2 * g_srd.step_delay) + rest) % (4 * g_srd.accel_count + 1); /* 计算余数,下次计算补上余数,减少误差 */
/* 检查是否为最后一步 */
if (g_srd.accel_count >= 0) /* 判断减速步数是否从负值加到0是的话 减速完成 */
{
g_srd.run_state = STOP;
}
break;
default:
break;
}
g_srd.step_delay = new_step_delay; /* 为下个(新的)延时(脉冲周期)赋值 */
}
}
}
4.运行效果
[localvideo]5b514252dc78a1a624a1aa5bfefd8d22[/localvideo]
5.附件
-
回复了主题帖:
测评汇总:航芯高性能MCU系列:ACM32F403开发板
本帖最后由 xld0932 于 2025-4-14 09:25 编辑
- 2025-04-06
-
发表了主题帖:
[国产芯AGM新品AG32VH407]5.熟悉CPLD开发过程
参考《AG32下CPLD的使用入门》手册进行一步步熟悉操作:
1.打开example样例程序, 默认是没有打开自定义 ip 的。首先, 要通过配置打开自定义 ip,方法是在 platformio.ini 中打开以下两项:
ip_name = analog_ip
logic_dir = logic
注意: 这两行去掉注释的时候, 前边不要留空格。 要顶格写。
2.打开以上两项后, 才能在左边栏看到创建 logic 框架工程的选项(prepare LOGIC)
3.点击该功能【Prepare LOGIC】 后, 可以看到在 example 工程目录下生成一个 logic 文件夹,自动生成的文件如下图:
4.使用前边安装的 Quartus II, 打开 example\logic 下的工程。如果是初次使用 Quartus II, 在安装完器件库后, AG32 要选用器件库中里 Cyclone IV E 的EP4CE75F23C8 来模拟。下面是创建工程和添加文件
5.工程转换:把 cpld 模板工程真正转换成 quartus 实际运行的状态。操作步骤:打开 Quartus 的 tools->TCL Scripts, 如下图所示:
6.运行成功后, 并没有看到该 logic 占用的资源数量,在上述创建工程之后也没有系统的 alta_sim.v,有哪位大拿知道,我上面是创建工程的时候出错了吗?
7.之前一直做的是MCU部分的开发,对于CPLD还是头一回接触,看来要把这个玩起来,还得先学习一下CPLD相关的知识了
- 2025-03-31
-
回复了主题帖:
[国产芯AGM新品AG32VH407]4.部署AG32 CPLD开发环境及开发流程
参考资料:
-
回复了主题帖:
[国产芯AGM新品AG32VH407]4.部署AG32 CPLD开发环境及开发流程
Quartus软件添加设备
1.下载对应的.qdz文件
2.在Quartus软件中添加设备
-
发表了主题帖:
[国产芯AGM新品AG32VH407]4.部署AG32 CPLD开发环境及开发流程
1.准备工作
纯粹用做CPLD开发时,我们需要准备2款软件,分别是Supra和Quartus;我们可以到相应官网上去下载并进行安装……Quartus工具软件是用来编写vlog代码并导出vo文件的,而Supra工具软件则来使vo文件生成最终的烧录bin文件的。
2.Supra软件部署
安装了SDK后,Supra.exe软件已经在SDK路径下了,可以直接使用,不需要再做下载安装这套流程了;具体路径如下所示:
3.Quartus软件部署
到Altera官网下载Quartus软件,最好使用最新版本的软件,不能使用Lite版本的,需要使用Full版本的;具体安装过程,可以根据软件的安装向导,一步一步进行安装,具体步骤如下所示:
4.结合VSCode实现CPLD开发流程的简要描述
在 VSCode里定义好 VE 配置;
在 VSCode里使用 prepare LOGIC 命令, 生成 CPLD的工程框架;
用 Quartus 打开该工程并添加自己的逻辑代码, 最后转换工程, 生成 vo;
用 Supra 打开转换后的工程, 编译出 BIN。
5.基础了解(摘录于《AG32下cpld的使用入门.pdf》)
有助于后面CPLD,以及MCU+CPLD的协同开发
AG32 整颗芯片包含两部分: mcu 和 cpld。这两部分是相互独立的(各自编译、 各自下载) , 但又可以相互连通起来(信号连通) 。芯片要把这两部分的 bin 都烧录进去, 才能运行起来。
mcu 和 cpld 到外部 PIN 脚的连通, 是通过 VE 来配置的。跟 ST 芯片每个 GPIO 固定对应某个 PIN 脚不同, 在 AG32 中, 所有的 GPIO/大部分的外设,对外引脚并不是定死的。 而是需要在 VE 文件中指定对应。VE 中, 除了配置 GPIO 到 PIN 的关联, 还可以配置 mcu 到 cpld 之间的信号关联。
Mcu+cpld 联合编程时, cpld 工程是由 vscode 的“prepare LOGIC” 命令自动生成的。注意: 联合编程时, cpld 工程不能手工通过 supra 建立。cpld 的操作也是依托 vscode 工程来的, 不再是孤立的。cpld 中的 top module 的信号输入, 是关联到 mcu 工程的(由 VE 配置出来) 。
cpld 在联合编程中的定位:整颗芯片运行时, 需要两个 bin: mcu 的 bin 和 cpld 的 bin。如果芯片中只使用 mcu 不使用 cpld:此时, VE 文件里只配置 mcu 用到的 PIN 脚即可。这种情况下, vscode 工程中点“upload LOGIC” 时, 会自动生成默认 logic(该 logic 中“用户逻辑” 为空而已) , 并编译出 bin 并烧录。整个过程中, logic 部分对开发者来说是无感的。如果芯片中同时使用 mcu 和 cpld:那么工程配置是“自定义 logic” 。此时 VE 文件里需要配置用到的 mcu 和 pin 之间、 mcu 和 cpld 之间、 cpld 和 pin 之间, 三种情况下的信号关联。这种情况下, vsode 下点“prepare LOGIC” 按钮, 会为开发者生成 cpld 的框架工程。 开发者需要在这个框架下完成 cpld 逻辑的编写。 这个编写调试中, 开发者持有主动权。 等全部开发完成, 并编译出 bin, 烧录就又回到 VSCODE 去烧录。这个过程, 除了 logic 需要用户自己编写编译外, 更外层的流程还是跟“默认 Logic” 是相同的。
-
回复了主题帖:
[国产芯AGM新品AG32VH407]3.熟悉PlatformIO工程配置,实现流水灯的编译、调试、烧录
参考资料:
-
回复了主题帖:
[国产芯AGM新品AG32VH407]3.熟悉PlatformIO工程配置,实现流水灯的编译、调试、烧录
目前下载调试只支持 jlink 和 AGM 专用下载器(cmsis-dap)
jlink-openocd:2 线模式(TCK,TMS)
jlink-openoce-jtag:标准模式
cmsis-dap-openocd:2 线模式(TCK,TMS)
cmsis-dap-openocd-jtag:标注模式
Serial:串口下载(只能下载)需要 Boot0 拉高下载
备注提醒:jlink 需要单独安装插件才可以使用,安装兼容插件后,其他原本使用 jlink 的芯片就无法使用了,必须卸载重新安装后才可以,这里推荐使用 AGM专用下载器(免驱使用更方面)
-
回复了主题帖:
[国产芯AGM新品AG32VH407]3.熟悉PlatformIO工程配置,实现流水灯的编译、调试、烧录
关于 VE 级,CPLD 相关的配置,基本与封装来区别的
AGRV2KL100: 对应型号 AG32VF407VGT6,AG32VF303VCT6
AGRV2KL64: 对应型号 AG32VF407RGT6
AGRV2KL64H: 对应型号 AG32VH407RCT6
AGRV2KL48: 对应型号 AG32VF303CCT6
AGRV2KQ32: 对应型号 AG32VF303KCU6
-
回复了主题帖:
[国产芯AGM新品AG32VH407]3.熟悉PlatformIO工程配置,实现流水灯的编译、调试、烧录
芯片的 FLASH 对标选择
agrv2K_103/303(FLASH 为 256K)---对应型号:AG32VF303CCT6,AG32VF303KCU6,AG32VF303VCT6 AG32VH407RCT6
agrv2K_407(FLASH 为 1M)---对应型号:AG32VF407RGT6,AG32VF407VGT6
-
回复了主题帖:
[国产芯AGM新品AG32VH407]3.熟悉PlatformIO工程配置,实现流水灯的编译、调试、烧录
新开发板的第一次烧录,必须先烧录VE,再烧录程序!!!
AG32烧录程序和烧录VE配置是分开的;程序有改动,就编译程序后烧录程序;VE有改动,就烧录VE;烧录VE配置,其实就是烧录logic.bin(CPLD部分的bin)
-
发表了主题帖:
[国产芯AGM新品AG32VH407]3.熟悉PlatformIO工程配置,实现流水灯的编译、调试、烧录
1.原理图
首先我们先熟悉一下原理图的部分引脚,在下面的实现过程中,我们使用到了UART0串口调试打印功能,和LED灯实现流水灯的效果,对应原理图上的控制IO分别是IO42\IO43以及IO39~IO36
2.将开发板通道TYPE-C进行供电,将AGM调试器连接电脑与开发板2端,当电脑连接到PC后,会在电脑上检测到CMSIS-DAP的设备
3.打开Visual Studio Code软件,点击左侧边栏的PlatformIO图标
4.点击PROJECT TASKS下的Create New Project按钮,在右侧会显示PIO Home界面
5.点击PIO Home界面右侧的Projects选项卡,添加已有的工程选择Add Existing,创建新的工程选择Create New Project
6.我们选择打开已有的官方例程,如下所示:
7.工程打开后,如下所示,右侧资源管理器中会显示代码源文件及一些配置文件
8.配置platform.ini文件中的工程参数
9.修改VE配置
10.实现用户功能代码
int main(void)
{
// This will init clock and uart on the board
board_init();
// The default isr table is plic_isr. The default entries in the table are peripheral name based like CAN0_isr() or
// GPIO0_isr(), and can be re-assigned.
plic_isr[BUT_GPIO_IRQ] = Button_isr;
// Any interrupt priority needs to be greater than MIN_IRQ_PRIORITY to be effective
INT_SetIRQThreshold(MIN_IRQ_PRIORITY);
// Enable interrupt from BUT_GPIO
INT_EnableIRQ(BUT_GPIO_IRQ, PLIC_MAX_PRIORITY);
// TestMtimer(500);
// TestAnalog();
// TestCan();
// TestCrc();
// TestFcb();
// TestGpTimer();
// TestGpTimerPwm();
// TestI2c();
// TestRTC();
// TestSpi();
// TestSystem();
// TestTimer();
// TestWdog();
// TestUart();
// TestFlash();
//TestGpio();
printf("\r\nGPIO LED Demo...");
SYS_EnableAPBClock(APB_MASK_GPIO4);
GPIO_SetOutput(GPIO4, GPIO_BIT1 | GPIO_BIT2 | GPIO_BIT3 | GPIO_BIT4);
GPIO_SetLow(GPIO4, GPIO_BIT1 | GPIO_BIT2 | GPIO_BIT3 | GPIO_BIT4);
while(1)
{
GPIO_Toggle(GPIO4, GPIO_BIT1); UTIL_IdleUs(100e3);
GPIO_Toggle(GPIO4, GPIO_BIT2); UTIL_IdleUs(100e3);
GPIO_Toggle(GPIO4, GPIO_BIT3); UTIL_IdleUs(100e3);
GPIO_Toggle(GPIO4, GPIO_BIT4); UTIL_IdleUs(100e3);
}
}
11.编译工程
12.下载程序
13.下载VE配置
14.运行结果
15.运行效果
[localvideo]6ef0f35160bc94f96d3cf03fa4d20fe6[/localvideo]
- 2025-03-26
-
发表了主题帖:
[航芯高性能MCU系列:ACM32F403开发板]3.熟悉编码器模式,实现光电编码器的信号采集
1.熟悉TIM2定时器编码器模式(摘录于航芯 ACM32F403_A403 用户手册)
通用定时器 TIM2 由一个 32 位的自动装载计数器组成,它由一个可编程的预分频器驱动。它适合多种用途,包含测量输入信号的脉冲宽度(输入捕获),或者产生输出波形(输出比较、PWM 等)。 高级控制定时器和通用定时器是完全独立的,它们不共享任何资源,但它们可以同步操作。
编码器接口模式基本上相当于使用了一个带有方向选择的外部时钟。在这个模式下,计数器依照增量编码器的速度和方向被自动的修改,因此计数器的内容始终指示着编码器的位置。计数方向与相连的传感器旋转的方向对应。表格 8-1 列出了所有可能的组合,假设 TI1 和 TI2 不同时变换。
两个输入 TI1 和 TI2 被用来作为增量编码器的接口。假定计数器已经启动(TIMx_CR1 寄存器中的 CEN=1),则计数器由每次在 TI1FP1 或 TI2FP2 上的有效跳变驱动。 TI1FP1 和 TI2FP2是 TI1 和 TI2 在通过输入滤波器和极性控制后的信号;如果没有滤波和变相,则 TI1FP1=TI1, TI2FP2=TI2。根据两个输入信号的跳变顺序,产生了计数脉冲和方向信号。依据两个输入信号的跳变顺序,计数器向上或向下计数,同时硬件对 TIMx_CR1 寄存器的 DIR 位进行相应的设置。不管计数器是依靠 TI1 计数、依靠 TI2 计数或者同时依靠 TI1 和 TI2 计数,在任一输入端(TI1 或者 TI2)的跳变都会重新计算 DIR 位。
一个外部的增量编码器可以直接与 MCU 连接而不需要外部接口逻辑。但是,一般会使用比较器将编码器的差动输出转换到数字信号,这大大增加了抗噪声干扰能力。编码器输出的第三个信号表示机械零点,可以把它连接到一个外部中断输入并触发一个计数器复位。 是一个计数器操作的实例,显示了计数信号的产生和方向控制。它还显示了当选择了双边沿时,输入抖动是如何被抑制的;抖动可能会在传感器的位置靠近一个转换点时产生。
2.实现功能
2.1.初始化编码器,通过电机的转动带动编码器工作,打印编码器的数值
2.2.通过编码器的数值及方向变化来控制电机运行的方向切换
3.代码实现
3.1.初始化编码器
#include "bsp_encoder.h"
void bsp_EncoderInit(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_Base_InitTypeDef TIM_Base_InitStruct;
TIM_IC_InitTypeDef TIM_IC_InitStruct;
System_Module_Enable(EN_GPIOAB);
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Alternate = GPIO_FUNCTION_3;
GPIO_Init(GPIOA, &GPIO_InitStruct);
System_Module_Enable(EN_TIM2);
TIM_Base_InitStruct.Prescaler = 0;
TIM_Base_InitStruct.Period = 0xFFFFFFFF;
TIM_Base_InitStruct.RepetitionCounter = 0;
TIM_Base_InitStruct.CounterMode = TIM_COUNTERMODE_UP;
TIM_Base_InitStruct.ClockDivision = TIM_CLOCKDIVISION_DIV1;
TIM_TimeBase_Init(TIM2, &TIM_Base_InitStruct);
TIM_SelectSlaveMode(TIM2, TIM_SLAVE_MODE_ENC1);
TIM_TI1FP1_ConfigInputStage(TIM2, TIM_SLAVE_CAPTURE_ACTIVE_FALLING, TIM_TI1_FILTER_LVL(6));
TIM_IC_InitStruct.ICPolarity = TIM_SLAVE_CAPTURE_ACTIVE_FALLING;
TIM_IC_InitStruct.ICSelection = TIM_ICSELECTION_DIRECTTI;
TIM_IC_InitStruct.ICPrescaler = TIM_IC1_PRESCALER_1;
TIM_IC_InitStruct.TIFilter = TIM_TI1_FILTER_LVL(6);
TIM_ICInit(TIM2, &TIM_IC_InitStruct, TIM_CHANNEL_1);
TIM2->CNT = 0;
TIM_Cmd(TIM2, ENABLE);
}
3.2.功能实现代码
#include "main.h"
#include "key.h"
#include "motor.h"
#include "platform.h"
#include "bsp_led.h"
#include "bsp_motor.h"
int main(void)
{
#if 0
uint32_t OldCount = 0;
uint32_t NewCount = 0;
#else
uint8_t EncoderDir = 0;
int32_t EncoderCount = 0;
#endif
System_Init();
PLATFORM_Init();
bsp_LedInit();
KEY_Init();
MOTOR_Init();
while (1)
{
#if 0
NewCount = TIM2->CNT;
if (OldCount != NewCount)
{
OldCount = NewCount;
printf("\r\nDIR : %d, CNT : %d", (TIM2->CR1 & 0x00000010), NewCount);
}
#else
EncoderCount = TIM2->CNT;
if (EncoderDir == 0)
{
if (EncoderCount > 5000)
{
EncoderDir = 1;
bsp_MotorSwitchDirection();
}
}
else
{
if (EncoderCount < 0)
{
EncoderDir = 0;
bsp_MotorSwitchDirection();
}
}
#endif
}
}
4.测试结果
5.运行效果
[localvideo]9392fa4d76da8c86e434a35e4fb1b8e2[/localvideo]
6.程序附件
- 2025-03-23
-
加入了学习《DIY作品演示》,观看 基于MQTT消息通讯的IoT入门实例
- 2025-03-12
-
回复了主题帖:
[兆易GD32H759I-EVAL]8.熟悉ENET接口,通过移植lwip协议,实现TCPUDP通讯功能
-
发表了主题帖:
[兆易GD32H759I-EVAL]8.熟悉ENET接口,通过移植lwip协议,实现TCPUDP通讯功能
1.以太网(ENET)
GD32H759系列MCU带有1~2路以太网接口,该以太网模块包含两个10/100Mbps以太网MAC(媒体访问控制器),采用DMA优化数据帧的发送与接收性能,支持MII(媒体独立接口)与RMII(简化的媒体独立接口)两种与物理层(PHY)通讯的标准接口,实现以太网数据帧的发送与接收。以太网模块遵守IEEE 802.3-2002标准和IEEE 1588-2008标准。主要特性如下:
MAC特性
支持10Mbps或100Mbps数据传输速率;
支持MII和RMII接口;
支持调试用回环模式;
支持符合CSMA/CD协议的半双工背压通讯;
支持符合IEEE 802.3x的流控通讯。在当前帧发送完毕后,根据接收的暂停帧中暂停时间延迟发送。在全双工/半双工模式下, MAC根据RxFIFO的填充程度自动发送暂停帧/背压信号;
支持符合IEEE 802.3x的全双工流控通讯,当输入流控信号失效时,自动发送零时间片暂停帧。支持符合IEEE 802.3x的半双工流控通讯,支持根据RxFIFO的填充程度(直通模式)自动发送背压信号;
可选择在发送操作时自动生成校验/填充位;
可选择在接收操作时自动去除校验/填充位;
帧长度可配置;
帧间隙可配置;
支持多种模式的接收过滤;
支持检测接收帧的IEEE 802.1Q VLAN标签;
支持强制网络统计标准(RFC2819/RFC2665) ;
支持两种唤醒帧检测: LAN远程唤醒帧和AMD的Magic PacketTM帧;
支持校验和检查(IPv4报头, IPv4或IPv6数据格式封装的TCP、 UDP或ICMP) ;
支持IEEE 1588-2008标准定义的以太网帧时间戳,并将其按64位记录于帧状态中;
相互独立的两个FIFO分别用于发送与接收;
在延迟冲突、过度冲突、过度顺延和下溢情况下丢弃帧;
帧传输时,支持存储转发模式下的硬件校验和的计算和插入。
DMA特性
支持环结构或链结构两种形式的描述符列表;
每个描述符可以传输最高为8192字节的数据;
中断可配置,适用于多种工作状态;
支持轮询或固定优先级两种方式仲裁DMA发送和接收控制器的请求。
PTP特性
支持符合IEEE1588的时间同步功能;
支持粗/精调两种校正方法;输出秒脉冲;
达到预设目标时间时触发中断。
2.以太网模块框图
3.搭建硬件环境
使用一个交换机一个端口连接到路由器上,一个端口连接到GD32H759I-EVAL开发板的以太网接口上,给交换机供电;将GD32H759I-EVAL开发板上关于ENET功能的跳帽设置正确。
4.实现功能
基于官网提供的例程熟悉ENET配置,熟悉Lwip移植和TCP/UDP通讯功能的实现。
5.程序设计
5.1.ENET外设配置
#include "gd32h7xx_enet.h"
#include "gd32h7xx_enet_eval.h"
#include "main.h"
const uint8_t gd32_str[] =
{
"\r\n############ Welcome GigaDevice ############\r\n"
};
static __IO uint32_t enet_init_status = 0;
static void enet_gpio_config(void);
static void enet_mac_dma_config(void);
#ifdef USE_ENET_INTERRUPT
static void nvic_configuration(void);
#endif
void enet_system_setup(void)
{
uint32_t ahb_frequency = 0;
#ifdef USE_ENET_INTERRUPT
nvic_configuration();
#endif
enet_gpio_config();
enet_mac_dma_config();
if (0 == enet_init_status)
{
while (1)
{
}
}
#ifdef USE_ENET_INTERRUPT
#ifdef USE_ENET0
enet_interrupt_enable(ENET0, ENET_DMA_INT_NIE);
enet_interrupt_enable(ENET0, ENET_DMA_INT_RIE);
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
enet_desc_select_enhanced_mode(ENET0);
#endif
#endif
#ifdef USE_ENET1
enet_interrupt_enable(ENET1, ENET_DMA_INT_NIE);
enet_interrupt_enable(ENET1, ENET_DMA_INT_RIE);
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
enet_desc_select_enhanced_mode(ENET1);
#endif
#endif
#endif
systick_clksource_set(SYSTICK_CLKSOURCE_CKSYS);
ahb_frequency = rcu_clock_freq_get(CK_AHB);
SysTick_Config(ahb_frequency / 100);
}
static void enet_mac_dma_config(void)
{
ErrStatus reval_state = ERROR;
#ifdef USE_ENET0
rcu_periph_clock_enable(RCU_ENET0);
rcu_periph_clock_enable(RCU_ENET0TX);
rcu_periph_clock_enable(RCU_ENET0RX);
enet_deinit(ENET0);
reval_state = enet_software_reset(ENET0);
if (ERROR == reval_state)
{
while (1)
{
}
}
#ifdef CHECKSUM_BY_HARDWARE
enet_init_status = enet_init(ENET0, ENET_AUTO_NEGOTIATION, ENET_AUTOCHECKSUM_DROP_FAILFRAMES, ENET_BROADCAST_FRAMES_PASS);
#else
enet_init_status = enet_init(ENET0, ENET_AUTO_NEGOTIATION, ENET_NO_AUTOCHECKSUM, ENET_BROADCAST_FRAMES_PASS);
#endif
#endif
#ifdef USE_ENET1
rcu_periph_clock_enable(RCU_ENET1);
rcu_periph_clock_enable(RCU_ENET1TX);
rcu_periph_clock_enable(RCU_ENET1RX);
enet_deinit(ENET1);
reval_state = enet_software_reset(ENET1);
if (ERROR == reval_state)
{
while (1)
{
}
}
#ifdef CHECKSUM_BY_HARDWARE
enet_init_status = enet_init(ENET1, ENET_AUTO_NEGOTIATION, ENET_AUTOCHECKSUM_DROP_FAILFRAMES, ENET_BROADCAST_FRAMES_PASS);
#else
enet_init_status = enet_init(ENET1, ENET_AUTO_NEGOTIATION, ENET_NO_AUTOCHECKSUM, ENET_BROADCAST_FRAMES_PASS);
#endif
#endif
}
#ifdef USE_ENET_INTERRUPT
static void nvic_configuration(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
#ifdef USE_ENET0
nvic_irq_enable(ENET0_IRQn, 0, 0);
#endif
#ifdef USE_ENET1
nvic_irq_enable(ENET1_IRQn, 0, 0);
#endif
}
#endif
static void enet_gpio_config(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_GPIOC);
rcu_periph_clock_enable(RCU_GPIOD);
rcu_periph_clock_enable(RCU_GPIOE);
rcu_periph_clock_enable(RCU_GPIOG);
rcu_periph_clock_enable(RCU_GPIOH);
gpio_af_set(GPIOA, GPIO_AF_0, GPIO_PIN_8);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8);
rcu_periph_clock_enable(RCU_SYSCFG);
#ifdef MII_MODE
#ifdef PHY_CLOCK_MCO
rcu_ckout0_config(RCU_CKOUT0SRC_HXTAL, RCU_CKOUT0_DIV1);
#endif
#ifdef USE_ENET0
syscfg_enet_phy_interface_config(ENET0, SYSCFG_ENET_PHY_MII);
#endif
#ifdef USE_ENET1
syscfg_enet_phy_interface_config(ENET1, SYSCFG_ENET_PHY_MII);
#endif
#elif defined RMII_MODE
rcu_ckout0_config(RCU_CKOUT0SRC_PLL0P, RCU_CKOUT0_DIV12);
#ifdef USE_ENET0
syscfg_enet_phy_interface_config(ENET0, SYSCFG_ENET_PHY_RMII);
#endif
#ifdef USE_ENET1
syscfg_enet_phy_interface_config(ENET1, SYSCFG_ENET_PHY_RMII);
#endif
#endif
#ifdef USE_ENET0
#ifdef MII_MODE
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_1);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_2);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7);
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_1);
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_2);
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_7);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_10);
gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_8);
gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_10);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_1);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_2);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_3);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_4);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_5);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_1);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_2);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_3);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_4);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_5);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_2);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_3);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7);
gpio_af_set(GPIOH, GPIO_AF_11, GPIO_PIN_2);
gpio_af_set(GPIOH, GPIO_AF_11, GPIO_PIN_3);
gpio_af_set(GPIOH, GPIO_AF_11, GPIO_PIN_6);
gpio_af_set(GPIOH, GPIO_AF_11, GPIO_PIN_7);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14);
gpio_af_set(GPIOG, GPIO_AF_11, GPIO_PIN_11);
gpio_af_set(GPIOG, GPIO_AF_11, GPIO_PIN_13);
gpio_af_set(GPIOG, GPIO_AF_11, GPIO_PIN_14);
gpio_mode_set(GPIOD, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_8);
#elif defined RMII_MODE
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_1);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_2);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7);
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_1);
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_2);
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_7);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12);
gpio_af_set(GPIOG, GPIO_AF_11, GPIO_PIN_11);
gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_12);
gpio_af_set(GPIOG, GPIO_AF_11, GPIO_PIN_12);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_1);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_4);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_5);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_1);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_4);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_5);
#endif
#endif
#ifdef USE_ENET1
#ifdef MII_MODE
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_10);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_15);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_15);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_6);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_7);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_8);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_9);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_10);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_11);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_12);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_13);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_14);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_15);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_15);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_15);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_6);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_9);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_11);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_12);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_13);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_14);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_15);
gpio_mode_set(GPIOE, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_1);
#elif defined RMII_MODE
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_8);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_9);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_11);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_12);
gpio_af_set(GPIOH, GPIO_AF_6, GPIO_PIN_14);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_6);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_11);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_13);
gpio_af_set(GPIOG, GPIO_AF_6, GPIO_PIN_14);
#endif
#endif
}
5.2.Lwip接口实现
#include "lwip/mem.h"
#include "netif/etharp.h"
#include "ethernetif.h"
#include "gd32h7xx_enet.h"
#include "main.h"
#include <string.h>
#define IFNAME0 'G'
#define IFNAME1 'D'
extern enet_descriptors_struct rxdesc_tab[ENET_RXBUF_NUM], txdesc_tab[ENET_TXBUF_NUM];
extern uint8_t rx_buff[ENET_RXBUF_NUM][ENET_RXBUF_SIZE];
extern uint8_t tx_buff[ENET_TXBUF_NUM][ENET_TXBUF_SIZE];
extern enet_descriptors_struct *dma_current_txdesc;
extern enet_descriptors_struct *dma_current_rxdesc;
enet_descriptors_struct ptp_txstructure[ENET_TXBUF_NUM];
enet_descriptors_struct ptp_rxstructure[ENET_RXBUF_NUM];
static void low_level_init(struct netif *netif)
{
#ifdef CHECKSUM_BY_HARDWARE
int i;
#endif
netif->hwaddr_len = ETHARP_HWADDR_LEN;
netif->hwaddr[0] = MAC_ADDR0;
netif->hwaddr[1] = MAC_ADDR1;
netif->hwaddr[2] = MAC_ADDR2;
netif->hwaddr[3] = MAC_ADDR3;
netif->hwaddr[4] = MAC_ADDR4;
netif->hwaddr[5] = MAC_ADDR5;
#ifdef USE_ENET0
enet_mac_address_set(ENET0, ENET_MAC_ADDRESS0, netif->hwaddr);
#endif
#ifdef USE_ENET1
enet_mac_address_set(ENET1, ENET_MAC_ADDRESS0, netif->hwaddr);
#endif
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
#ifdef USE_ENET0
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
enet_ptp_enhanced_descriptors_chain_init(ENET0, ENET_DMA_TX);
enet_ptp_enhanced_descriptors_chain_init(ENET0, ENET_DMA_RX);
#else
enet_descriptors_chain_init(ENET0, ENET_DMA_TX);
enet_descriptors_chain_init(ENET0, ENET_DMA_RX);
#endif
#endif
#ifdef USE_ENET1
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
enet_ptp_enhanced_descriptors_chain_init(ENET1, ENET_DMA_TX);
enet_ptp_enhanced_descriptors_chain_init(ENET1, ENET_DMA_RX);
#else
enet_descriptors_chain_init(ENET1, ENET_DMA_TX);
enet_descriptors_chain_init(ENET1, ENET_DMA_RX);
#endif
#endif
{
int i;
for (i = 0; i < ENET_RXBUF_NUM; i++)
{
enet_rx_desc_immediate_receive_complete_interrupt(&rxdesc_tab[i]);
}
}
#ifdef CHECKSUM_BY_HARDWARE
for (i = 0; i < ENET_TXBUF_NUM; i++)
{
enet_transmit_checksum_config(&txdesc_tab[i], ENET_CHECKSUM_TCPUDPICMP_FULL);
}
#endif
#ifdef USE_ENET0
enet_enable(ENET0);
#endif
#ifdef USE_ENET1
enet_enable(ENET1);
#endif
}
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
struct pbuf *q;
int framelength = 0;
uint8_t *buffer;
while ((uint32_t)RESET != (dma_current_txdesc->status & ENET_TDES0_DAV))
{
}
#ifdef USE_ENET0
buffer = (uint8_t *)(enet_desc_information_get(ENET0, dma_current_txdesc, TXDESC_BUFFER_1_ADDR));
#endif
#ifdef USE_ENET1
buffer = (uint8_t *)(enet_desc_information_get(ENET1, dma_current_txdesc, TXDESC_BUFFER_1_ADDR));
#endif
for (q = p; q != NULL; q = q->next)
{
memcpy((uint8_t *)&buffer[framelength], q->payload, q->len);
framelength = framelength + q->len;
}
#ifdef USE_ENET0
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
ENET_NOCOPY_PTPFRAME_TRANSMIT_ENHANCED_MODE(ENET0, framelength, NULL);
#else
ENET_NOCOPY_FRAME_TRANSMIT(ENET0, framelength);
#endif
#endif
#ifdef USE_ENET1
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
ENET_NOCOPY_PTPFRAME_TRANSMIT_ENHANCED_MODE(ENET1, framelength, NULL);
#else
ENET_NOCOPY_FRAME_TRANSMIT(ENET1, framelength);
#endif
#endif
return (ERR_OK);
}
static struct pbuf *low_level_input(struct netif *netif)
{
struct pbuf *p, *q;
u16_t len;
int l = 0;
uint8_t *buffer;
p = NULL;
#ifdef USE_ENET0
len = enet_desc_information_get(ENET0, dma_current_rxdesc, RXDESC_FRAME_LENGTH);
buffer = (uint8_t *)(enet_desc_information_get(ENET0, dma_current_rxdesc, RXDESC_BUFFER_1_ADDR));
#endif
#ifdef USE_ENET1
len = enet_desc_information_get(ENET1, dma_current_rxdesc, RXDESC_FRAME_LENGTH);
buffer = (uint8_t *)(enet_desc_information_get(ENET1, dma_current_rxdesc, RXDESC_BUFFER_1_ADDR));
#endif
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
if (p != NULL)
{
for (q = p; q != NULL; q = q->next)
{
memcpy((uint8_t *)q->payload, (u8_t *)&buffer[l], q->len);
l = l + q->len;
}
}
#ifdef USE_ENET0
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
ENET_NOCOPY_PTPFRAME_RECEIVE_ENHANCED_MODE(ENET0, NULL);
#else
ENET_NOCOPY_FRAME_RECEIVE(ENET0);
#endif
#endif
#ifdef USE_ENET1
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
ENET_NOCOPY_PTPFRAME_RECEIVE_ENHANCED_MODE(ENET1, NULL);
#else
ENET_NOCOPY_FRAME_RECEIVE(ENET1);
#endif
#endif
return (p);
}
err_t ethernetif_input(struct netif *netif)
{
err_t err;
struct pbuf *p;
p = low_level_input(netif);
if (p == NULL)
{
return (ERR_MEM);
}
err = netif->input(p, netif);
if (err != ERR_OK)
{
LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
pbuf_free(p);
p = NULL;
}
return (err);
}
err_t ethernetif_init(struct netif *netif)
{
LWIP_ASSERT("netif != NULL", (netif != NULL));
#if LWIP_NETIF_HOSTNAME
netif->hostname = "Gigadevice.COM_lwip";
#endif
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
netif->output = etharp_output;
netif->linkoutput = low_level_output;
low_level_init(netif);
return (ERR_OK);
}
5.3.网络配置
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/tcp.h"
#include "lwip/udp.h"
#include "netif/etharp.h"
#include "lwip/dhcp.h"
#include "ethernetif.h"
#include "stdint.h"
#include "main.h"
#include "netconf.h"
#include <stdio.h>
#include "lwip/priv/tcp_priv.h"
#include "lwip/timeouts.h"
#define MAX_DHCP_TRIES 4
typedef enum
{
DHCP_START = 0,
DHCP_WAIT_ADDRESS,
DHCP_ADDRESS_ASSIGNED,
DHCP_TIMEOUT
} dhcp_state_enum;
#ifdef USE_DHCP
uint32_t dhcp_fine_timer = 0;
uint32_t dhcp_coarse_timer = 0;
dhcp_state_enum dhcp_state = DHCP_START;
#endif
struct netif g_mynetif0, g_mynetif1;
uint32_t tcp_timer = 0;
uint32_t arp_timer = 0;
ip_addr_t ip_address =
{
0
};
void lwip_dhcp_process_handle(void);
void lwip_netif_status_callback(struct netif *netif);
void lwip_stack_init(void)
{
ip_addr_t ipaddr;
ip_addr_t netmask;
ip_addr_t gw;
mem_init();
memp_init();
#ifdef TIMEOUT_CHECK_USE_LWIP
sys_timeouts_init();
#endif
#ifdef USE_DHCP
ipaddr.addr = 0;
netmask.addr = 0;
gw.addr = 0;
#else
IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
#endif
#ifdef USE_ENET0
netif_add(&g_mynetif0, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);
netif_set_default(&g_mynetif0);
netif_set_status_callback(&g_mynetif0, lwip_netif_status_callback);
netif_set_up(&g_mynetif0);
#endif
#ifdef USE_ENET1
netif_add(&g_mynetif1, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);
netif_set_default(&g_mynetif1);
netif_set_status_callback(&g_mynetif1, lwip_netif_status_callback);
netif_set_up(&g_mynetif1);
#endif
}
void lwip_pkt_handle0(void)
{
ethernetif_input(&g_mynetif0);
}
void lwip_pkt_handle1(void)
{
ethernetif_input(&g_mynetif1);
}
void lwip_periodic_handle(__IO uint32_t localtime)
{
#if LWIP_TCP
if (localtime - tcp_timer >= TCP_TMR_INTERVAL)
{
tcp_timer = localtime;
tcp_tmr();
}
#endif
if ((localtime - arp_timer) >= ARP_TMR_INTERVAL)
{
arp_timer = localtime;
etharp_tmr();
}
#ifdef USE_DHCP
if (localtime - dhcp_fine_timer >= DHCP_FINE_TIMER_MSECS)
{
dhcp_fine_timer = localtime;
dhcp_fine_tmr();
if ((DHCP_ADDRESS_ASSIGNED != dhcp_state) && (DHCP_TIMEOUT != dhcp_state))
{
lwip_dhcp_process_handle();
}
}
if (localtime - dhcp_coarse_timer >= DHCP_COARSE_TIMER_MSECS)
{
dhcp_coarse_timer = localtime;
dhcp_coarse_tmr();
}
#endif
}
#ifdef USE_DHCP
void lwip_dhcp_process_handle(void)
{
ip_addr_t ipaddr;
ip_addr_t netmask;
ip_addr_t gw
;
#ifdef USE_ENET0
struct dhcp *dhcp_client0;
#endif
#ifdef USE_ENET1
struct dhcp *dhcp_client1;
#endif
#ifdef USE_ENET0
switch (dhcp_state)
{
case DHCP_START:
dhcp_start(&g_mynetif0);
dhcp_state = DHCP_WAIT_ADDRESS;
break;
case DHCP_WAIT_ADDRESS:
ip_address.addr = g_mynetif0.ip_addr.addr;
if (0 != ip_address.addr)
{
dhcp_state = DHCP_ADDRESS_ASSIGNED;
printf("\r\nDHCP -- eval board ip address: %d.%d.%d.%d \r\n",
ip4_addr1_16(&ip_address), ip4_addr2_16(&ip_address),
ip4_addr3_16(&ip_address), ip4_addr4_16(&ip_address));
}
else
{
dhcp_client0 = netif_dhcp_data(&g_mynetif0);
if (dhcp_client0->tries > MAX_DHCP_TRIES)
{
dhcp_state = DHCP_TIMEOUT;
dhcp_stop(&g_mynetif0);
IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
netif_set_addr(&g_mynetif0, &ipaddr, &netmask, &gw);
}
}
break;
default:
break;
}
#endif
#ifdef USE_ENET1
switch (dhcp_state)
{
case DHCP_START:
dhcp_start(&g_mynetif1);
dhcp_state = DHCP_WAIT_ADDRESS;
break;
case DHCP_WAIT_ADDRESS:
ip_address.addr = g_mynetif1.ip_addr.addr;
if (0 != ip_address.addr)
{
dhcp_state = DHCP_ADDRESS_ASSIGNED;
printf("\r\nDHCP -- eval board ip address: %d.%d.%d.%d \r\n",
ip4_addr1_16(&ip_address), ip4_addr2_16(&ip_address),
ip4_addr3_16(&ip_address), ip4_addr4_16(&ip_address));
}
else
{
dhcp_client1 = netif_dhcp_data(&g_mynetif1);
if (dhcp_client1->tries > MAX_DHCP_TRIES)
{
dhcp_state = DHCP_TIMEOUT;
dhcp_stop(&g_mynetif1);
IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
netif_set_addr(&g_mynetif1, &ipaddr, &netmask, &gw);
}
}
break;
default:
break;
}
#endif
}
#endif
unsigned long sys_now(void)
{
extern volatile unsigned int g_localtime;
return (g_localtime);
}
5.4.TCP/UDP接口
#include "tcp_client.h"
#include "lwip/tcp.h"
#include "lwip/memp.h"
#include <string.h>
#include <stdio.h>
#include "gd32h7xx.h"
#include "main.h"
#define MAX_BUF_SIZE 50
struct recev_packet
{
int length;
char bytes[MAX_BUF_SIZE];
};
static err_t tcp_client_connected(void *arg, struct tcp_pcb *pcb, err_t err);
static err_t tcp_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
static err_t tcp_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
struct pbuf *q;
struct recev_packet *recev_packet = (struct recev_packet *)arg;
int buf_full;
char *c;
int i;
if (p != NULL)
{
tcp_recved(pcb, p->tot_len);
if (!recev_packet)
{
pbuf_free(p);
return (ERR_ARG);
}
buf_full = 0;
for (q = p; q != NULL; q = q->next)
{
c = q->payload;
for (i = 0; i < q->len && !buf_full; i++)
{
if (recev_packet->length < MAX_BUF_SIZE)
{
recev_packet->bytes[recev_packet->length] = c[i];
recev_packet->length++;
}
else
{
buf_full = 1;
}
}
}
tcp_write(pcb, recev_packet->bytes, recev_packet->length, 1);
recev_packet->length = 0;
pbuf_free(p);
}
else if (ERR_OK == err)
{
mem_free(recev_packet);
return (tcp_close(pcb));
}
return (ERR_OK);
}
static err_t tcp_client_connected(void *arg, struct tcp_pcb *pcb, err_t err)
{
tcp_arg(pcb, mem_calloc(sizeof(struct recev_packet), 1));
tcp_recv(pcb, tcp_client_recv);
return (ERR_OK);
}
void tcp_client_init(void)
{
struct tcp_pcb *pcb;
ip_addr_t ipaddr;
IP4_ADDR(&ipaddr, IP_S_ADDR0, IP_S_ADDR1, IP_S_ADDR2, IP_S_ADDR3);
pcb = tcp_new();
if (ERR_USE != tcp_bind(pcb, IP_ADDR_ANY, 10260))
{
tcp_connect(pcb, &ipaddr, 10260, tcp_client_connected);
}
else
{
printf("connect is still alive \r\n ");
memp_free(MEMP_TCP_PCB, pcb);
}
}
#include "udp_echo.h"
#include "lwip/udp.h"
#include <string.h>
#include <stdio.h>
#include "gd32h7xx.h"
static void udp_echo_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
static void udp_echo_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
ip_addr_t destaddr = *addr;
if (p != NULL)
{
udp_sendto(pcb, p, &destaddr, port);
pbuf_free(p);
}
}
void udp_echo_init(void)
{
struct udp_pcb *udppcb;
udppcb = udp_new();
udp_bind(udppcb, IP_ADDR_ANY, 1025);
udp_recv(udppcb, udp_echo_recv, NULL);
}
5.5.TCP/UDP应用实现
#include "hello_gigadevice.h"
#include "lwip/tcp.h"
#include <string.h>
#include <stdio.h>
#include "gd32h7xx.h"
#define GREETING \
"\n\r======= HelloGigaDevice =======\
\n\r== GD32 ==\
\n\r== Telnet SUCCESS==\
\n\rHello. What is your name?\r\n"
#define HELLO "\n\rGigaDevice Hello "
#define MAX_NAME_SIZE 32
extern const uint8_t gd32_str[];
struct name
{
int length;
char bytes[MAX_NAME_SIZE];
};
static err_t hello_gigadevice_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
static err_t hello_gigadevice_accept(void *arg, struct tcp_pcb *pcb, err_t err);
static void hello_gigadevice_conn_err(void *arg, err_t err);
static err_t hello_gigadevice_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
struct pbuf *q;
struct name *name = (struct name *)arg;
int done;
char *c;
int i;
if (p != NULL)
{
tcp_recved(pcb, p->tot_len);
if (!name)
{
pbuf_free(p);
return (ERR_ARG);
}
done = 0;
for (q = p; q != NULL; q = q->next)
{
c = q->payload;
for (i = 0; i < q->len && !done; i++)
{
done = ((c[i] == '\r') || (c[i] == '\n'));
if (name->length < MAX_NAME_SIZE)
{
name->bytes[name->length++] = c[i];
}
}
}
if (done)
{
if ((name->bytes[name->length - 2] != '\r') || (name->bytes[name->length - 1] != '\n'))
{
if (((name->bytes[name->length - 1] == '\r') ||
(name->bytes[name->length - 1] == '\n')) &&
(name->length + 1 <= MAX_NAME_SIZE))
{
name->length += 1;
}
else if (name->length + 2 <= MAX_NAME_SIZE)
{
name->length += 2;
}
else
{
name->length = MAX_NAME_SIZE;
}
name->bytes[name->length - 2] = '\r';
name->bytes[name->length - 1] = '\n';
}
tcp_write(pcb, HELLO, strlen(HELLO), 1);
tcp_write(pcb, name->bytes, name->length, TCP_WRITE_FLAG_COPY);
printf("\n\rGigaDevice\n\rTelnet %s %s", HELLO, name->bytes);
name->length = 0;
}
pbuf_free(p);
}
else if (err == ERR_OK)
{
mem_free(name);
return (tcp_close(pcb));
}
return (ERR_OK);
}
static err_t hello_gigadevice_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
u32_t ipaddress;
u8_t iptxt[50];
u8_t iptab[4];
ipaddress = pcb->remote_ip.addr;
printf("\n\rTelnet hello_gigadevice_accept:%d.%d.%d.%d %s",
(u8_t)(ipaddress),
(u8_t)(ipaddress >> 8),
(u8_t)(ipaddress >> 16),
(u8_t)(ipaddress >> 24), GREETING);
iptab[0] = (u8_t)(ipaddress >> 24);
iptab[1] = (u8_t)(ipaddress >> 16);
iptab[2] = (u8_t)(ipaddress >> 8);
iptab[3] = (u8_t)(ipaddress);
sprintf((char *)iptxt, "Telnet:%d.%d.%d.%d ", iptab[3], iptab[2], iptab[1], iptab[0]);
printf("%s\r\n", iptxt);
tcp_arg(pcb, mem_calloc(sizeof(struct name), 1));
tcp_err(pcb, hello_gigadevice_conn_err);
tcp_recv(pcb, hello_gigadevice_recv);
tcp_write(pcb, iptxt, strlen((char *)iptxt), 1);
sprintf((char *)iptxt, "You telnet computer's IP is: %d.%d.%d.%d\n", iptab[3], iptab[2], iptab[1], iptab[0]);
printf("%s\r\n", iptxt);
tcp_write(pcb, gd32_str, strlen((char *)gd32_str), 1);
tcp_write(pcb, GREETING, strlen(GREETING), 1);
return (ERR_OK);
}
void hello_gigadevice_init(void)
{
struct tcp_pcb *pcb;
pcb = tcp_new();
tcp_bind(pcb, IP_ADDR_ANY, 8000);
pcb = tcp_listen(pcb);
tcp_accept(pcb, hello_gigadevice_accept);
}
static void hello_gigadevice_conn_err(void *arg, err_t err)
{
struct name *name;
name = (struct name *)arg;
mem_free(name);
}
5.6.网络配置信息
#ifndef MAIN_H
#define MAIN_H
#include "gd32h7xx.h"
#include "stdint.h"
#include "gd32h7xx_enet_eval.h"
//#define USE_DHCP /* enable DHCP, if disabled static address is used */
//#define USE_ENET_INTERRUPT
/*The USE_ENET0 and USE_ENET1 macros cannot be opened at the same time*/
#define USE_ENET0
//#define USE_ENET1
//#define TIMEOUT_CHECK_USE_LWIP
/* MAC address: MAC_ADDR0:MAC_ADDR1:MAC_ADDR2:MAC_ADDR3:MAC_ADDR4:MAC_ADDR5 */
#define MAC_ADDR0 4
#define MAC_ADDR1 0xA
#define MAC_ADDR2 0xD
#define MAC_ADDR3 0xE
#define MAC_ADDR4 0xD
#define MAC_ADDR5 6
/* static IP address: IP_ADDR0.IP_ADDR1.IP_ADDR2.IP_ADDR3 */
#define IP_ADDR0 192
#define IP_ADDR1 168
#define IP_ADDR2 124
#define IP_ADDR3 210
/* remote IP address: IP_S_ADDR0.IP_S_ADDR1.IP_S_ADDR2.IP_S_ADDR3 */
#define IP_S_ADDR0 192
#define IP_S_ADDR1 168
#define IP_S_ADDR2 124
#define IP_S_ADDR3 17
/* net mask */
#define NETMASK_ADDR0 255
#define NETMASK_ADDR1 255
#define NETMASK_ADDR2 255
#define NETMASK_ADDR3 0
/* gateway address */
#define GW_ADDR0 192
#define GW_ADDR1 168
#define GW_ADDR2 124
#define GW_ADDR3 1
/* MII and RMII mode selection */
#define RMII_MODE // user have to provide the 50 MHz clock by soldering a 50 MHz oscillator
//#define MII_MODE
/* clock the PHY from external 25MHz crystal (only for MII mode) */
#ifdef MII_MODE
#define PHY_CLOCK_MCO
#endif
/* function declarations */
/* updates the system local time */
void time_update(void);
/* insert a delay time */
void delay_10ms(uint32_t ncount);
#endif /* MAIN_H */
6.运行结果
6.1.硬件环境连接完成后,下载程序到开发板运行,通过电脑终端命令,获取当前电脑的IP地址信息,PING通GD32H759I-EVAL开发板。
6.2.电脑作为TCP Client端,GD32H759I-EVAL开发板作为TCP Server端,进行连接后,运行测试结果如下:
6.3.电脑作为TCP Server端,GD32H759I-EVAL开发板作为TCP Client端,进行连接后,检测到GD32H759I-EVAL开发板Client上线,Server端和Client端互发消息成功,运行测试结果如下:
6.4.电脑和GD32H759I-EVAL开发板都使用UDP进行通讯,配置电脑的IP地址及端口号之后,向指定的GD32H759I-EVAL开发板的IP地址和端口号发送数据,并接收到了GD32H759I-EVAL开发板的回复消息,测试运行结果如下所示:
7.附件
软件工程:
测试工具:
- 2025-03-11
-
回复了主题帖:
[航芯高性能MCU系列:ACM32F403开发板]2.熟悉高级定时器,实现对高精密步进电机的驱动
lugl4313820 发表于 2025-3-11 18:03
这个运行非常平稳,有没有准备搞个加速的算法我们学习学习。
有的,后面准备了……
-
发表了主题帖:
[航芯高性能MCU系列:ACM32F403开发板]2.熟悉高级定时器,实现对高精密步进电机的驱动
1.硬件环境准备
1.1.ACM32F403开发板
1.2.高精密步进电机套装(高精密步进电机、光电旋转编码器、TB6600驱动器、限位开关等)
1.3.接口板
用于连接高精密步进电机套装与ACM32F403开发板的连接
1.3.1.原理图
1.3.2.接口板3D效果图
2.硬件环境连接
ACM32F403开发板通过接口板与TB6600驱动器、光电旋转编码器、限位开关进行连接,将高精密步进电机与TB6600驱动器进行连接,接口板供12V电源。
3.TIM1定时器
高级控制定时器 TIM1 由一个 16 位的自动装载计数器组成,它由一个可编程的预分频器驱动。它适合多种用途,包含测量输入信号的脉冲宽度(输入捕获),或者产生输出波形(输出比较、 PWM、嵌入死区时间的互补 PWM 等)。使用定时器预分频器和系统时钟控制预分频器,可以实现脉冲宽度和波形周期从几个微秒到几个毫秒的调节。高级控制定时器和通用定时器是完全独立的,它们不共享任何资源,但它们可以同步操作。主要特性如下:
⚫ 16 位向上、向下、向上/下自动装载计数器
⚫ 16 位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为 1~65536 之间的任意数值
⚫ 多达 4 个独立通道
- 输入捕获
- 输出比较
- PWM 生成(边沿或中间对齐模式)
- 单脉冲模式输出
⚫ 死区时间可编程的互补输出
⚫ 使用外部信号控制定时器和定时器互联的同步电路
⚫ 允许在指定数目的计数器周期之后更新定时器寄存器的重复计数器
⚫ 刹车输入信号可以将定时器输出信号置于复位状态或者一个已知状态
⚫ 如下事件发生时产生中断/DMA:
- 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
- 触发事件: (计数器启动、停止、初始化或者由内部/外部触发计数)
- 输入捕获
- 输出比较
- 刹车信号输入
⚫ 支持针对定位的增量(正交)编码器和霍尔传感器电路
⚫ 触发输入作为外部时钟或者按周期的电流管理
4.实现功能
通过TIM1的通道1输出PWM作为TB6600驱动器的脉冲输入信号,控制电机转速;通过PC9控制TB6600的DIR输入,控制电机的转向;通过PC10和PC11作为限位开关的输入,当触发限位开关后,立即切换电机的运转方向。
5.程序实现
5.1.高精密电机驱动程序
#include "bsp_motor.h"
void bsp_MotorInitGPIO(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
System_Module_Enable(EN_GPIOCD);
/* Direction */
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = GPIO_FUNCTION_0;
GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_WriteBit(GPIOC, GPIO_PIN_9, Bit_SET);
/* Limit Switch */
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10 | GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = GPIO_FUNCTION_0;
GPIO_Init(GPIOC, &GPIO_InitStruct);
}
void bsp_MotorInitTIM1(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_Base_InitTypeDef TIM_Base_InitStruct;
TIM_OC_InitTypeDef TIM_OC_InitStruct;
uint32_t TIM_Clock;
System_Module_Enable(EN_GPIOCD);
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Alternate = GPIO_FUNCTION_2;
GPIO_Init(GPIOC, &GPIO_InitStruct);
System_Module_Enable(EN_TIM1);
if (System_Get_SystemClock() == System_Get_APBClock())
{
TIM_Clock = System_Get_APBClock();
}
else
{
TIM_Clock = System_Get_APBClock() * 2;
}
TIM_Base_InitStruct.Prescaler = (TIM_Clock / 1000000) - 1;
TIM_Base_InitStruct.Period = 1000 - 1;
TIM_Base_InitStruct.RepetitionCounter = 0;
TIM_Base_InitStruct.CounterMode = TIM_COUNTERMODE_UP;
TIM_Base_InitStruct.ClockDivision = TIM_CLOCKDIVISION_DIV1;
TIM_TimeBase_Init(TIM1, &TIM_Base_InitStruct);
TIM_OC_InitStruct.OCMode = OUTPUT_MODE_PWM1;
TIM_OC_InitStruct.Pulse = (TIM_Base_InitStruct.Period + 1) / 2;
TIM_OC_InitStruct.OCPolarity = OUTPUT_POL_ACTIVE_HIGH;
TIM_OC_InitStruct.OCNPolarity = OUTPUT_POL_ACTIVE_HIGH;
TIM_OC_InitStruct.OCFastMode = OUTPUT_FAST_MODE_DISABLE;
TIM_OC_InitStruct.OCIdleState = OUTPUT_IDLE_STATE_0;
TIM_OC_InitStruct.OCNIdleState = OUTPUT_IDLE_STATE_0;
TIM_OC1Init(TIM1, &TIM_OC_InitStruct);
TIM_CCxCmd(TIM1, TIM_CHANNEL_1, TIM_CCx_Enable);
TIM_Cmd(TIM1, ENABLE);
}
void bsp_MotorInit(void)
{
bsp_MotorInitGPIO();
bsp_MotorInitTIM1();
bsp_MotorRunning();
}
void bsp_MotorRunning(void)
{
TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
void bsp_MotorSuspend(void)
{
TIM_CtrlPWMOutputs(TIM1, DISABLE);
}
void bsp_MotorSwitchDirection(void)
{
bsp_MotorSuspend();
if (GPIO_ReadOutputDataBit(GPIOC, GPIO_PIN_9) == RESET)
{
GPIO_WriteBit(GPIOC, GPIO_PIN_9, Bit_SET);
}
else
{
GPIO_WriteBit(GPIOC, GPIO_PIN_9, Bit_RESET);
}
bsp_MotorRunning();
}
uint8_t bsp_MotorReadLimitSwitchPinLevel(uint8_t Index)
{
uint8_t PinLevel = 0;
switch (Index)
{
case 1:
PinLevel = GPIO_ReadInputDataBit(GPIOC, GPIO_PIN_10);
break;
case 2:
PinLevel = GPIO_ReadInputDataBit(GPIOC, GPIO_PIN_11);
break;
default:
break;
}
return (PinLevel);
}
5.2.限位开关逻辑控制程序
#include "key.h"
#include "bsp_key.h"
#include "bsp_motor.h"
#include "multi_button.h"
struct Button keyUser;
struct Button keyMotorRight;
struct Button keyMotorLeft;
volatile uint8_t KeyRegisterFlag = 0;
void KEY_UserHandler(void *btn)
{
struct Button *handle = (struct Button *)btn;
if (handle->button_id == 0)
{
printf("\r\n%s", __FUNCTION__);
}
}
void KEY_MotorHandler(void *btn)
{
struct Button *handle = (struct Button *)btn;
if ((handle->button_id == 1) || (handle->button_id == 2))
{
bsp_MotorSwitchDirection();
}
}
void KEY_Init(void)
{
bsp_KeyInit();
button_init(&keyUser, bsp_KeyReadPinLevel, RESET, 0);
button_attach(&keyUser, PRESS_DOWN, KEY_UserHandler);
button_start(&keyUser);
button_init(&keyMotorRight, bsp_MotorReadLimitSwitchPinLevel, RESET, 1);
button_attach(&keyMotorRight, PRESS_DOWN, KEY_MotorHandler);
button_start(&keyMotorRight);
button_init(&keyMotorLeft, bsp_MotorReadLimitSwitchPinLevel, RESET, 2);
button_attach(&keyMotorLeft, PRESS_DOWN, KEY_MotorHandler);
button_start(&keyMotorLeft);
KeyRegisterFlag = 1;
}
6.运行效果
[localvideo]47221c2b78b452f15e6c7da7a3fd095b[/localvideo]
7.工程源码