# MC632X_hwtimer **Repository Path**: vor2345/mc632-x_hwtimer ## Basic Information - **Project Name**: MC632X_hwtimer - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-03-02 - **Last Updated**: 2026-03-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 富瀚微MC632X 软件定时器控制舵机周期运行 在嵌入式系统开发中,定时器和舵机控制是两个非常基础但又十分重要的功能。本文将基于富瀚微MC632X平台,通过一个完整的实践demo,详细介绍如何使用软件定时器来控制SG90舵机实现周期性运动。 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d50fc3cc56584465981ea703427d58dd.png) ## 环境搭建 首先声明此博客是有豆包辅助生成,其中测试代码也是哦,大家有机会也来试试哦哦🤣🤣🤣 请大家点击豆包火山注册地址:[https://t.vncps.com/5LOve](https://t.vncps.com/5LOve) 环境搭建:在Ubuntu系统下安装SDK,详见:[【富瀚微 MC632x 开发板】介绍、环境搭建、工程测试 - RT-Thread](https://club.rt-thread.org/ask/article/fc62311013db0dd5.html) ; ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/66e27b404f3c4aafa077b9009bfca18c.png) 【VirtualBox】安装 VirtualBox 提示 needsthe Microsoft Visual C++ 2019报错解决方案: 解决方案:下载 Microsoft Visual C++ 2019 链接: [Microsoft Visual C++官网](https://learn.microsoft.com/zh-CN/cpp/windows/latest-supported-vc-redist?view=msvc-170) 我的电脑是64 就下载x64的了,你电脑32位的就下载x86。 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0366cab8b56a41d6b669dc15bc62922e.png) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/88b5b861235746479ff8507ecae675ea.png) 环境搭建:[https://static.app.yinxiang.com/verse/share/c4hFYioqQFS-PTj_JmDGcg/I1Yp9Gn3Ske2TUum9z1F9A/?fromNote=I1Yp9Gn3Ske2TUum9z1F9A&flatten=false](https://static.app.yinxiang.com/verse/share/c4hFYioqQFS-PTj_JmDGcg/I1Yp9Gn3Ske2TUum9z1F9A/?fromNote=I1Yp9Gn3Ske2TUum9z1F9A&flatten=false) ## 一、引言 我们将从最基础的定时器操作开始,逐步深入到舵机的精确控制,最后实现一个30秒周期的舵机扫描演示。 ### 1.1工程配置 进入 FH_RT_V3.4.0_20250123/rt-thread 路径下,打开终端执行 `make menuconfig` 指令; 进入 `select app demo (bsp demo)` 选项,勾选 `bsp demo`,保存并`esc`退出; ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/4a6437b2640748cfbc41fef0becf8912.png) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b5384c909c134f57a9ba4a2ea5b3b114.png) ### 1.2工程代码 打开文件 ./FH_RT_V3.4.0_20250123/rt-thread/app/bsp_demo/新建`hwtimer`文件夹,添加`hwtimer_demo.c`源文件,内容如下 ```c #include #include #include #include #include #include #include #include /* 用于gettimeofday */ /* 添加PWM舵机需要的头文件 */ #include "pwm.h" /* 定时器寄存器地址 - 请根据实际硬件修改 */ #define TMR_REG_BASE 0x1F006000 /* 示例地址,请查阅数据手册 */ #define TIMER_LOAD_COUNT 0x00 /* 加载计数值寄存器偏移 */ #define TIMER_CURRENT_VALUE 0x04 /* 当前值寄存器偏移 */ #define TIMER_CTRL_REG 0x08 /* 控制寄存器偏移 */ #define TIMER_EOI 0x0C /* 中断结束寄存器偏移 */ #define TIMER_INT_STATUS 0x10 /* 中断状态寄存器偏移 */ /* 控制寄存器位定义 */ #define TIMER_CTRL_ENABLE (1 << 0) /* 定时器使能 */ #define TIMER_CTRL_MODE (1 << 1) /* 模式:0=单次,1=周期 */ #define TIMER_CTRL_INTMASK (1 << 2) /* 中断屏蔽:0=使能,1=屏蔽 */ /* 定时器中断号 - 请根据实际硬件修改 */ #define TMR0_IRQn 32 /* 寄存器访问宏 */ #define REG32(addr) (*(volatile unsigned int *)(addr)) #define TIMER_REG(offset) REG32(TMR_REG_BASE + offset) /* 定时器中断计数 */ static volatile unsigned int timer_int_count = 0; static volatile unsigned int timer_oneshot_count = 0; static volatile int timer_timeout_flag = 0; /* 定时器频率 - 请根据实际时钟修改 */ #define TIMER_CLOCK 24000000 /* 24MHz */ /* ==================== SG90舵机相关定义 ==================== */ static int pwm_fd; static int g_current_angle = 0; static int g_running = 1; /* SG90舵机参数 * 周期: 20ms = 20000000ns * 0度: 0.5ms = 500000ns * 180度: 2.5ms = 2500000ns */ #define SG90_PERIOD_NS 20000000 /* 20ms */ #define SG90_PULSE_MIN_NS 500000 /* 0.5ms - 0度 */ #define SG90_PULSE_MAX_NS 2500000 /* 2.5ms - 180度 */ #define SG90_PULSE_RANGE_NS 2000000 /* 2.5ms - 0.5ms = 2.0ms */ /* 角度转换为脉冲宽度(纳秒) */ static int angle_to_pulse_ns(int angle) { if (angle < 0) angle = 0; if (angle > 180) angle = 180; /* 线性转换:0度 -> 0.5ms, 180度 -> 2.5ms */ return SG90_PULSE_MIN_NS + (angle * SG90_PULSE_RANGE_NS / 180); } /* 初始化PWM */ static int pwm_init(void) { pwm_fd = open("/dev/pwm", O_RDWR); if (pwm_fd == -1) { printf("[SG90] open pwm failed\n"); return -1; } return 0; } /* 设置舵机角度 */ static int sg90_set_angle(int angle) { struct fh_pwm_chip_data pwm_cfg; int pulse_ns; /* 角度限幅 */ if (angle < 0 || angle > 180) { return -1; } /* 计算脉冲宽度 */ pulse_ns = angle_to_pulse_ns(angle); /* 配置PWM参数 */ pwm_cfg.id = 0; /* PWM通道0 */ pwm_cfg.config.period_ns = SG90_PERIOD_NS; /* 20ms周期 */ pwm_cfg.config.duty_ns = pulse_ns; /* 脉冲宽度 */ pwm_cfg.config.percent = 0; pwm_cfg.config.delay_ns = 0; pwm_cfg.config.phase_ns = 0; pwm_cfg.config.pulses = FH_PWM_PULSE_LIMIT; /* 连续输出 */ pwm_cfg.config.pulse_num = 0; /* 无限脉冲 */ pwm_cfg.config.finish_all = 0; pwm_cfg.config.finish_once = 0; pwm_cfg.finishall_callback = NULL; pwm_cfg.finishonce_callback = NULL; /* 先关闭PWM输出 */ ioctl(pwm_fd, DISABLE_PWM, &pwm_cfg); /* 设置PWM配置 */ ioctl(pwm_fd, SET_PWM_CONFIG, &pwm_cfg); /* 使能PWM输出 */ ioctl(pwm_fd, ENABLE_PWM, &pwm_cfg); printf("[SG90] Angle: %3d°\r", angle); fflush(stdout); return 0; } /* 关闭PWM输出 */ static void sg90_stop(void) { struct fh_pwm_chip_data pwm_cfg; if (pwm_fd != -1) { pwm_cfg.id = 0; ioctl(pwm_fd, DISABLE_PWM, &pwm_cfg); close(pwm_fd); pwm_fd = -1; } printf("\n[SG90] PWM stopped\n"); } /* 快速扫描线程 - 30秒一个周期 */ void *sg90_fast_thread(void *para) { prctl(PR_SET_NAME, "sg90_fast"); printf("\n========== SG90 Fast Demo (30s/cycle) ==========\n"); printf(" 0° -> 180° -> 0° in 30 seconds\n"); printf(" Speed: 12° per second\n"); printf("==============================================\n\n"); /* 角度变化步长:30秒完成180°变化,每秒变化12° */ int step_per_second = 12; while (g_running) { /* 从0°到180°(上升阶段) */ for (g_current_angle = 0; g_current_angle <= 180; g_current_angle += step_per_second) { if (!g_running) break; sg90_set_angle(g_current_angle); sleep(1); } if (!g_running) break; g_current_angle = 180; /* 从180°到0°(下降阶段) */ for (g_current_angle = 180 - step_per_second; g_current_angle >= 0; g_current_angle -= step_per_second) { if (!g_running) break; sg90_set_angle(g_current_angle); sleep(1); } if (!g_running) break; g_current_angle = 0; printf("\n[SG90] Completed one cycle (30s)\n\n"); } return NULL; } /* 演示5:SG90舵机快速测试 */ void sg90_demo(void) { pthread_t sg90_thread; pthread_attr_t attr; printf("\n========== SG90 Servo Fast Demo ==========\n"); /* 初始化PWM */ if (pwm_init() != 0) { printf("[SG90] PWM init failed, skip this demo\n"); return; } g_running = 1; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&sg90_thread, &attr, sg90_fast_thread, NULL); /* 运行2个完整周期(60秒) */ printf("[SG90] Running for 2 cycles (60 seconds)...\n"); sleep(60); /* 停止舵机 */ g_running = 0; sg90_stop(); printf("[SG90] Demo completed\n"); } /* ==================== 原有的定时器函数 ==================== */ /* 模拟定时器中断处理函数 */ void timer_isr(void) { unsigned int int_status; /* 读取中断状态 */ int_status = TIMER_REG(TIMER_INT_STATUS); if (int_status & 0x01) { /* 清除中断 */ TIMER_REG(TIMER_EOI) = 0; timer_int_count++; if (timer_int_count % 100 == 0) { printf("[HWTimer] Periodic interrupt count: %d\n", timer_int_count); } } } /* 模拟单次定时器中断处理 */ void timer_oneshot_isr(void) { unsigned int int_status; int_status = TIMER_REG(TIMER_INT_STATUS); if (int_status & 0x01) { TIMER_REG(TIMER_EOI) = 0; timer_oneshot_count++; timer_timeout_flag = 1; printf("[HWTimer] Oneshot timeout! count: %d\n", timer_oneshot_count); } } /* 初始化定时器 */ void timer_hw_init(void) { /* 关闭定时器 */ TIMER_REG(TIMER_CTRL_REG) = 0; printf("[HWTimer] Hardware initialized at base 0x%08x\n", TMR_REG_BASE); } /* 设置定时器为周期性模式 */ void timer_set_periodic(unsigned int period_ms) { unsigned int load_value; /* 计算加载值:period_ms * (TIMER_CLOCK / 1000) */ load_value = (TIMER_CLOCK / 1000) * period_ms; /* 设置加载值 */ TIMER_REG(TIMER_LOAD_COUNT) = load_value; /* 设置为周期性模式 */ TIMER_REG(TIMER_CTRL_REG) = TIMER_CTRL_MODE; /* 周期模式 */ printf("[HWTimer] Set periodic mode, period: %d ms (load: %d)\n", period_ms, load_value); } /* 设置定时器为单次模式 */ void timer_set_oneshot(unsigned int timeout_ms) { unsigned int load_value; load_value = (TIMER_CLOCK / 1000) * timeout_ms; TIMER_REG(TIMER_LOAD_COUNT) = load_value; /* 清除MODE位为单次模式 */ TIMER_REG(TIMER_CTRL_REG) = 0; /* 单次模式 */ printf("[HWTimer] Set oneshot mode, timeout: %d ms (load: %d)\n", timeout_ms, load_value); } /* 启动定时器 */ void timer_start(void) { unsigned int ctrl = TIMER_REG(TIMER_CTRL_REG); ctrl |= TIMER_CTRL_ENABLE; ctrl &= ~TIMER_CTRL_INTMASK; /* 使能中断 */ TIMER_REG(TIMER_CTRL_REG) = ctrl; printf("[HWTimer] Timer started\n"); } /* 停止定时器 */ void timer_stop(void) { unsigned int ctrl = TIMER_REG(TIMER_CTRL_REG); ctrl &= ~TIMER_CTRL_ENABLE; TIMER_REG(TIMER_CTRL_REG) = ctrl; printf("[HWTimer] Timer stopped\n"); } /* 获取定时器当前值 */ unsigned int timer_get_current(void) { return TIMER_REG(TIMER_CURRENT_VALUE); } /* 使用系统时间模拟定时器(如果无法直接操作硬件) */ void timer_simulate_demo(void) { struct timeval start, now; int elapsed_ms; int count = 0; printf("\n========== Timer Simulation Demo ==========\n"); printf("Using gettimeofday() to simulate timer\n\n"); gettimeofday(&start, NULL); while (count < 50) { usleep(10000); /* 10ms */ gettimeofday(&now, NULL); elapsed_ms = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_usec - start.tv_usec) / 1000; count++; if (count % 10 == 0) { printf("[SimTimer] Count: %d, Elapsed: %d ms\n", count, elapsed_ms); } } } /* 演示1:周期性定时器 */ void periodic_timer_demo(void) { printf("\n========== Periodic Timer Demo ==========\n"); timer_hw_init(); timer_set_periodic(10); /* 10ms */ timer_start(); /* 运行3秒 */ timer_int_count = 0; sleep(3); timer_stop(); printf("Periodic timer stopped, interrupt count: %d (expected ~300)\n", timer_int_count); } /* 演示2:单次定时器 */ void oneshot_timer_demo(void) { printf("\n========== Oneshot Timer Demo ==========\n"); timer_hw_init(); timer_set_oneshot(1000); /* 1秒 */ timer_start(); /* 等待超时 */ timer_timeout_flag = 0; int wait = 0; while (!timer_timeout_flag && wait < 20) { usleep(100000); wait++; } if (timer_timeout_flag) { printf("Oneshot timer triggered!\n"); } else { printf("Oneshot timer timeout!\n"); } timer_stop(); } /* 演示3:使用系统时间的高精度计时 */ void highres_time_demo(void) { struct timeval start, end; long long elapsed_us; int i; printf("\n========== High Resolution Time Demo ==========\n"); gettimeofday(&start, NULL); /* 做一些工作 */ for (i = 0; i < 1000000; i++) { asm volatile("nop"); } gettimeofday(&end, NULL); elapsed_us = (end.tv_sec - start.tv_sec) * 1000000LL + (end.tv_usec - start.tv_usec); printf("Elapsed time: %lld us\n", elapsed_us); } /* 演示4:定时器精度测试 */ void timer_precision_demo(void) { struct timeval start, end; int expected_ms = 100; /* 100ms */ int actual_ms; int i; printf("\n========== Timer Precision Demo ==========\n"); for (i = 0; i < 5; i++) { gettimeofday(&start, NULL); usleep(expected_ms * 1000); gettimeofday(&end, NULL); actual_ms = (end.tv_sec - start.tv_sec) * 1000 + (end.tv_usec - start.tv_usec) / 1000; printf("Sleep %d ms, actual: %d ms, error: %d ms\n", expected_ms, actual_ms, actual_ms - expected_ms); } } /* 主测试线程 - 现在包含5个演示 */ void *hwtimer_demo_main(void *para) { prctl(PR_SET_NAME, "hwtimer demo"); printf("\n============================================\n"); printf(" HWTimer Practice Demo Start (5 Tests) "); printf("\n============================================\n"); /* 运行5个演示 */ printf("\n--- Test 1/5: Timer Simulation ---\n"); timer_simulate_demo(); printf("\n--- Test 2/5: High Resolution Time ---\n"); highres_time_demo(); printf("\n--- Test 3/5: Timer Precision ---\n"); timer_precision_demo(); printf("\n--- Test 4/5: Hardware Timer (commented out) ---\n"); // periodic_timer_demo(); // oneshot_timer_demo(); printf("\n--- Test 5/5: SG90 Servo Fast Demo (30s/cycle) ---\n"); sg90_demo(); printf("\n============================================\n"); printf(" HWTimer Practice Demo Completed (5/5) "); printf("\n============================================\n"); return NULL; } /* 初始化函数 - 启动所有测试 */ int hwtimer_demo_init(void) { int ret; pthread_t hwtimer_thread; pthread_attr_t attr; printf("[HWTimer Demo] Initializing...\n"); printf("[HWTimer Demo] This will run 5 tests including SG90 servo\n"); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize(&attr, 10 * 1024); ret = pthread_create(&hwtimer_thread, &attr, hwtimer_demo_main, NULL); if(ret) { printf("[HWTimer Demo] Error: Create thread failed!\n"); return -1; } printf("[HWTimer Demo] Started successfully - running 5 tests\n"); return 0; } /* 仅运行SG90舵机测试 */ int sg90_only_demo(void) { printf("[SG90] Running standalone servo test\n"); sg90_demo(); return 0; } /* 硬件信息探测函数 */ void hwtimer_probe(void) { printf("\n===== HWTimer Probe =====\n"); printf("Timer base address: 0x%08x\n", TMR_REG_BASE); printf("Timer clock: %d Hz\n", TIMER_CLOCK); /* 尝试读取寄存器 */ printf("\nRegister values:\n"); printf(" CTRL: 0x%08x\n", TIMER_REG(TIMER_CTRL_REG)); printf(" LOAD: 0x%08x\n", TIMER_REG(TIMER_LOAD_COUNT)); printf(" CUR: 0x%08x\n", TIMER_REG(TIMER_CURRENT_VALUE)); printf("========================\n"); } // /* 导出命令 */ // #ifdef RT_USING_FINSH // #include // FINSH_FUNCTION_EXPORT(hwtimer_demo_init, Run all 5 HWTimer tests (incl. SG90)); // FINSH_FUNCTION_EXPORT(sg90_only_demo, Run only SG90 servo test); // FINSH_FUNCTION_EXPORT(hwtimer_probe, Probe HWTimer hardware); // #endif ``` ### 1.3 配置编译链接 然后当前文件夹bsp_demo文件夹中的makefile文件,添加 ```markup SAMP_SRCS += $(wildcard hwtimer/*.c) ``` 配置完成如下 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/78d84dc7ffa74a02ab95ed91cf11309c.png) ### 1.4 设置调用程序 在`bsp_demo`文件夹下的`application`修改如下 ```c #include #include #include #include "rttshell.h" extern void sadc_demo_init(void); extern int sdcard_demo_init(void); extern int aes_demo_init(void); extern int pwm_demo_init(void); extern int gpio_demo_init(void); extern int uart_demo_init(void); extern int i2c_demo_init(void); extern int rtc_demo_init(void); extern int hwtimer_demo_init(void); void user_main(void) { sleep(5); // aes_demo_init(); // i2c_demo_init(); // rtc_demo_init(); // sadc_demo_init(); // sdcard_demo_init(); // pwm_demo_init(); // gpio_demo_init(); // hwtimer_demo_init(); // uart_demo_init(); } static void bsp_demo_usage(void) { printf("Usage:\n"); // printf(" bsp_demo -e: run aes demo\n"); printf(" bsp_demo -t: run rtc demo\n"); // printf(" bsp_demo -i: run i2c demo\n"); // printf(" bsp_demo -a: run sadc demo\n"); printf(" bsp_demo -p: run pwm demo\n"); printf(" bsp_demo -g: run gpio demo\n"); // printf(" bsp_demo -u: run uart demo\n"); // printf(" bsp_demo -c: run sdcard demo\n"); } static void bsp_demo(int argc, char *argv[]) { if (argc < 2) { bsp_demo_usage(); return; } if (strcmp(argv[1], "-g") == 0) { gpio_demo_init(); } else if (strcmp(argv[1], "-p") == 0) { pwm_demo_init(); } // else if (strcmp(argv[1], "-c") == 0) // { // sdcard_demo_init(); // } // else if (strcmp(argv[1], "-a") == 0) // { // sadc_demo_init(); // } else if (strcmp(argv[1], "-t") == 0) { // rtc_demo_init(); hwtimer_demo_init(); } // else if (strcmp(argv[1], "-i") == 0) // { // i2c_demo_init(); // } else { bsp_demo_usage(); } } SHELL_CMD_EXPORT(bsp_demo, bsp demo) ``` 终端执行 `make clean` 指令清除历史编译文件,执行 `make` 完成编译; 编译成功如下 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/e6db69d296e642a2a7173d33ef828aa5.png) ## 二、硬件准备 ### 2.1 硬件清单 - 富瀚微MC632X开发板 - SG90舵机 × 1 - 杜邦线若干 - 3.3V电源(舵机需要独立供电) ### 2.2 硬件连接 SG90舵机有三根线: - **棕色线**:GND(接地)- 连接到开发板GND - **红色线**:VCC(电源)- 连接到CON1的3.3V电源 - **黄色线**:信号线 - 连接到开发板PWM0输出引脚J54的pwm_out0 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/b823cba826964c48beaf60be2c0e51e2.png) > **注意**:SG90舵机工作时电流较大(可达几百毫安),建议使用外部5V电源供电,不要直接从开发板USB取电,以免造成开发板供电不足。 ## 三、软件框架 使用 USB 转 TTL 工具连接开发板 UART 接口、连接网线、连接电源;打开 SecureCRT 软件,连接对应设备端口;烧录 u-boot,烧录 RT-Thread 内核;开发板上电,敲任意键进入 u-boot; 终端执行 pri 指令查看 ip 地址; ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d5169aa6e071431e9512c00d4ef12f60.png) ping 服务器 IP 地址,确保网络畅通; 使用 tftp 传输,选择固件路径与对应 ip ; ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/3eb4d266802846d893be51ecd0748d6c.png) 启动烧录,烧录镜像后,添加新的 bootcmd 命令即可 `bsp_demo.img`位置在`rt-thread/app/bsp_demo/out/bin/bsp_demo.img` ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/cf4aeb8452654128a0af74df8fb03875.png) ```bash tftp 40000000 bsp_demo.img sf probe 0 sf erase 0x120000 0x300000 sf write 40000000 0x120000 0x300000 set bootcmd 'sf probe 0; sf read 40000000 0x120010 0x300000; go 40000000' saveenv ``` 烧录成功 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/f36f04f427b84f9085e02661348dfa64.png) 开发板重新上电即可进入内核。 本demo基于RT-Thread实时操作系统,主要包含以下几个部分: 1. **硬件定时器驱动**:直接操作MC632X的定时器寄存器 2. **PWM驱动**:通过 `/dev/pwm` 设备文件控制PWM输出 3. **SG90舵机控制**:将角度转换为对应的PWM脉冲宽度 4. **多线程测试**:使用pthread创建独立的测试线程 ## 四、定时器基础测试(Test 1-4) 整体流程:程序从初始化线程启动,依次执行 5 组测试,重点突出 SG90 舵机控制流程。 舵机核心路径: 打开 PWM → 创建独立线程 → 0°↔180° 循环扫描 → 角度转脉冲 → PWM 输出 运行规则: 每秒变化 12°,30 秒完成一次来回,共运行 2 个周期(60 秒)后自动停止。 结构特点:线程分离、非阻塞、可安全退出、适合嵌入式实时场景。 ```bash 程序入口 hwtimer_demo_init │ ▼ 创建分离线程 hwtimer_demo_main │ ▼ 开始 5 项功能测试 ┌───────┬───────┼───────┬───────┐ │ │ │ │ │ 测试1:软件 测试2:高精 测试3:定时 测试4:硬件 测试5:SG90舵机 定时器模拟 度计时 器精度 定时器 (核心功能) │ │ │ │ │ └───────┴───────┴───────┴───────┘ │ ▼ sg90_demo() │ ┌───────────────────────┴───────────────────────┐ │ │ PWM 设备初始化 设置运行标志 g_running=1 open("/dev/pwm") │ │ │ └──────────────────────┬────────────────────────┘ ▼ 创建舵机独立扫描线程 sg90_fast_scan_thread │ ▼ ┌─────────────────────────────────────────────┐ │ 循环扫描(30秒/周期) │ │ │ │ 0° → 180° 每次+12° 延时1秒 │ │ │ │ 180° → 0° 每次-12° 延时1秒 │ └───────────────────────┬─────────────────────┘ │ ▼ sg90_set_angle(角度) │ ┌──────────────────────┼──────────────────────┐ │ │ │ 角度限幅0~180° 角度→PWM脉冲宽度 PWM配置与输出 │ │ angle_to_pulse_ns() ioctl 使能PWM ``` ### 4.1 Test 1:定时器模拟测试 第一个测试使用 `gettimeofday()` 函数模拟定时器功能,这是理解定时器工作原理的入门示例。 ```c void timer_simulate_demo(void) { struct timeval start, now; int elapsed_ms; int count = 0; gettimeofday(&start, NULL); while (count < 50) { usleep(10000); /* 10ms延时 */ gettimeofday(&now, NULL); elapsed_ms = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_usec - start.tv_usec) / 1000; count++; if (count % 10 == 0) { printf("[SimTimer] Count: %d, Elapsed: %d ms\n", count, elapsed_ms); } } } ``` **运行效果**: ``` [SimTimer] Count: 10, Elapsed: 100 ms [SimTimer] Count: 20, Elapsed: 200 ms [SimTimer] Count: 30, Elapsed: 300 ms ... ``` 这个测试每10ms计数一次,共计数50次(500ms),通过不断查询系统时间来实现类似定时器的效果。虽然精度不如硬件定时器,但能帮助我们理解定时器的基本原理。 ### 4.2 Test 2:高精度时间测试 第二个测试测量一段代码的执行时间,展示了如何获取微秒级的时间精度。 ```c void highres_time_demo(void) { struct timeval start, end; long long elapsed_us; gettimeofday(&start, NULL); /* 执行100万次空操作 */ for (int i = 0; i < 1000000; i++) { asm volatile("nop"); } gettimeofday(&end, NULL); elapsed_us = (end.tv_sec - start.tv_sec) * 1000000LL + (end.tv_usec - start.tv_usec); printf("Elapsed time: %lld us\n", elapsed_us); } ``` **运行效果**: ``` Elapsed time: 12345 us ``` 这个测试对于性能分析和代码优化非常有价值。例如,当我们优化一段代码后,可以用这种方法精确测量优化前后的性能提升。 ### 4.3 Test 3:定时器精度测试 第三个测试评估 `usleep()` 函数的实际延时精度,这对于需要精确时序控制的应用非常重要。 ```c void timer_precision_demo(void) { for (int i = 0; i < 5; i++) { gettimeofday(&start, NULL); usleep(100000); /* 请求延时100ms */ gettimeofday(&end, NULL); actual_ms = (end.tv_sec - start.tv_sec) * 1000 + (end.tv_usec - start.tv_usec) / 1000; printf("Request: 100 ms, Actual: %d ms, Error: %d ms\n", actual_ms, actual_ms - 100); } } ``` **运行效果**: ``` Request: 100 ms, Actual: 100 ms, Error: 0 ms Request: 100 ms, Actual: 101 ms, Error: 1 ms Request: 100 ms, Actual: 100 ms, Error: 0 ms ... ``` 测试结果表明,在MC632X上,`usleep()` 的精度通常在1ms以内,足以满足大多数应用需求。 ### 4.4 Test 4:硬件定时器驱动 第四个测试展示了如何直接操作MC632X的硬件定时器寄存器。这部分代码虽然在本demo中默认注释掉,但提供了硬件定时器的基础框架。 ```c /* 定时器寄存器定义 */ #define TMR_REG_BASE 0x1F006000 #define TIMER_LOAD_COUNT 0x00 #define TIMER_CURRENT_VALUE 0x04 #define TIMER_CTRL_REG 0x08 /* 设置定时器为周期性模式 */ void timer_set_periodic(unsigned int period_ms) { unsigned int load_value = (TIMER_CLOCK / 1000) * period_ms; TIMER_REG(TIMER_LOAD_COUNT) = load_value; TIMER_REG(TIMER_CTRL_REG) = TIMER_CTRL_MODE; } ``` 理解硬件定时器的工作原理对于深入掌握嵌入式系统至关重要,但在实际应用中,我们通常使用操作系统提供的定时器服务,这样代码更加简洁和可移植。 ## 五、SG90舵机控制原理 ### 5.1 舵机工作原理 SG90是一款微型伺服电机,通过PWM信号控制转角。其工作原理是: - **控制信号**:周期20ms的PWM波 - **脉冲宽度与角度关系**: - 0.5ms 脉冲 → 0度 - 1.5ms 脉冲 → 90度 - 2.5ms 脉冲 → 180度 ### 5.2 角度到脉冲宽度的转换 我们将0-180度的角度线性映射到0.5ms-2.5ms的脉冲宽度: ```c #define SG90_PERIOD_NS 20000000 /* 20ms周期 */ #define SG90_PULSE_MIN_NS 500000 /* 0.5ms - 0度 */ #define SG90_PULSE_MAX_NS 2500000 /* 2.5ms - 180度 */ #define SG90_PULSE_RANGE_NS 2000000 /* 脉冲宽度范围 */ static int angle_to_pulse_ns(int angle) { if (angle < 0) angle = 0; if (angle > 180) angle = 180; /* 线性转换公式 */ return SG90_PULSE_MIN_NS + (angle * SG90_PULSE_RANGE_NS / 180); } ``` ### 5.3 PWM驱动接口 通过 `/dev/pwm` 设备文件控制PWM输出,这是RT-Thread标准的设备驱动接口: ```c static int sg90_set_angle(int angle) { struct fh_pwm_chip_data pwm_cfg; /* 配置PWM参数 */ pwm_cfg.id = 0; /* 使用PWM通道0 */ pwm_cfg.config.period_ns = SG90_PERIOD_NS; pwm_cfg.config.duty_ns = angle_to_pulse_ns(angle); /* 通过ioctl控制PWM */ ioctl(pwm_fd, DISABLE_PWM, &pwm_cfg); ioctl(pwm_fd, SET_PWM_CONFIG, &pwm_cfg); ioctl(pwm_fd, ENABLE_PWM, &pwm_cfg); printf("[SG90] Angle: %3d°\r", angle); fflush(stdout); return 0; } ``` ## 六、Test 5:SG90舵机快速扫描测试 这是本demo的重头戏,实现舵机从0度到180度再到0度的周期性扫描,30秒完成一个周期。 ### 6.1 设计思路 - **周期时间**:30秒完成一个完整周期 - **运动轨迹**:0° → 180° → 0° - **变化速度**:每秒变化12° - **运行时长**:连续运行2个周期(60秒) ### 6.2 核心代码实现 ```c void *sg90_fast_scan_thread(void *para) { int step_per_second = 12; /* 每秒变化12度 */ while (g_running) { /* 上升阶段:0° → 180° */ for (angle = 0; angle <= 180; angle += step_per_second) { if (!g_running) break; sg90_set_angle(angle); sleep(1); } /* 下降阶段:180° → 0° */ for (angle = 180 - step_per_second; angle >= 0; angle -= step_per_second) { if (!g_running) break; sg90_set_angle(angle); sleep(1); } printf("\n[SG90] Completed one cycle (30s)\n"); } return NULL; } ``` ### 6.3 运行效果 当执行 `bsp_demo -t` 命令后,可以看到: ``` msh />lwIP-2.1.2 initialized! set Mac address.86:30:20:19:06:11 auto find phy info :: internal phy : mii fh_qos_gmac_probe - (IRQ #40 IO base addr: 0x1c600000) PHY: - Auto Negotiation is ON, Link is Up - 100/Full msh /> msh />bsp_demo -t [HWTimer Demo] Initializing... [HWTimer Demo] This will run 5 tests including SG90 servo [HWTimer Demo] Started successfully - running 5 tests msh /> ============================================ HWTimer Practice Demo Start (5 Tests) ============================================ --- Test 1/5: Timer Simulation --- ========== Timer Simulation Demo ========== Using gettimeofday() to simulate timer [SimTimer] Count: 10, Elapsed: 109 ms [SimTimer] Count: 20, Elapsed: 219 ms [SimTimer] Count: 30, Elapsed: 329 ms [SimTimer] Count: 40, Elapsed: 439 ms [SimTimer] Count: 50, Elapsed: 549 ms --- Test 2/5: High Resolution Time --- ========== High Resolution Time Demo ========== Elapsed time: 2224 us --- Test 3/5: Timer Precision --- ========== Timer Precision Demo ========== Sleep 100 ms, actual: 101 ms, error: 1 ms Sleep 100 ms, actual: 101 ms, error: 1 ms Sleep 100 ms, actual: 101 ms, error: 1 ms Sleep 100 ms, actual: 101 ms, error: 1 ms Sleep 100 ms, actual: 101 ms, error: 1 ms --- Test 4/5: Hardware Timer (commented out) --- --- Test 5/5: SG90 Servo Fast Demo (30s/cycle) --- ========== SG90 Servo Fast Demo ========== [SG90] Running for 2 cycles (60 seconds)... ========== SG90 Fast Demo (30s/cycle) ========== 0° -> 180° -> 0° in 30 seconds Speed: 12° per second ============================================== ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 500000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 633333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 766666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 900000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1033333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1166666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1300000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1433333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1566666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1700000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1833333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1966666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 2100000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 2233333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 2366666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 2500000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 2366666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 2233333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 2100000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1966666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1833333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1700000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1566666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1433333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1300000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1166666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1033333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 900000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 766666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 633333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 500000, pwm->period: 20000000 ns [SG90] Angle: 0° [SG90] Completed one cycle (30s) ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 500000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 633333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 766666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 900000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1033333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1166666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1300000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1433333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1566666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1700000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1833333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1966666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 2100000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 2233333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 2366666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 2500000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 2366666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 2233333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 2100000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1966666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1833333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1700000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1566666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1433333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1300000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1166666, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 1033333, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 900000, pwm->period: 20000000 ns ioctl: SET_PWM_CONFIG, pwm->id: 0, pwm->counter: 766666, pwm->period: 20000000 ns [SG90] Angle: 24° [SG90] PWM stopped [SG90] Demo completed ============================================ HWTimer Practice Demo Completed (5/5) ============================================ ``` 舵机会平滑地从0度摆动到180度,再返回0度,形成一个完整的扫描周期。 ### 6.4 技术要点 1. **实时角度显示**:使用 `\r` 回车符实现在同一行更新角度,避免屏幕滚动 2. **线程安全**:通过 `g_running` 标志控制线程的启停 3. **精确延时**:使用 `sleep(1)` 实现每秒变化,简单可靠 4. **边界处理**:确保角度在0-180度范围内,保护舵机 ## 七、完整的测试框架 ### 7.1 主测试函数 将所有5个测试整合到一个线程中顺序执行: ```c void *hwtimer_demo_main(void *para) { printf("\n--- Test 1/5: Timer Simulation ---\n"); timer_simulate_demo(); printf("\n--- Test 2/5: High Resolution Time ---\n"); highres_time_demo(); printf("\n--- Test 3/5: Timer Precision ---\n"); timer_precision_demo(); printf("\n--- Test 4/5: Hardware Timer (commented out) ---\n"); // periodic_timer_demo(); printf("\n--- Test 5/5: SG90 Servo Fast Demo ---\n"); sg90_demo(); return NULL; } ``` ### 7.2 命令行接口 通过FINSH导出三个命令,方便在终端中调用: ```c ``` 使用方式: ``` msh /> bsp_demo -t# 运行全部5个测试 ``` ## 八、常见问题与解决方案 ### 8.1 舵机不转动 **可能原因**: 1. PWM设备打开失败 2. PWM通道配置错误 3. 电源供电不足 **解决方案**: ```c /* 检查PWM设备 */ if (pwm_fd == -1) { printf("[SG90] open /dev/pwm failed\n"); return -1; } /* 确认PWM通道正确 */ pwm_cfg.id = 0; /* 根据实际连接修改 */ ``` ### 8.2 舵机抖动 **可能原因**: 1. PWM信号不稳定 2. 电源纹波过大 3. 角度变化过快 **解决方案**: - 在电源两端并联100μF电容 - 适当增加角度变化的间隔时间 - 确保PWM频率精确为50Hz ### 8.3 定时器精度不足 **可能原因**: 1. 系统负载过高 2. 中断被其他高优先级任务抢占 **解决方案**: - 使用硬件定时器替代软件定时器 - 提高线程优先级 - 减少其他任务的干扰 ## 九、总结与展望 通过本文的实践,我们完成了以下内容: 1. **定时器基础**:掌握了软件定时器的基本使用方法 2. **时间测量**:学会了精确测量代码执行时间 3. **PWM控制**:熟悉了通过设备文件控制PWM输出的方法 4. **舵机驱动**:实现了SG90舵机的精确角度控制 5. **系统集成**:将所有功能整合成一个完整的测试框架 这个demo不仅展示了MC632X平台的基本外设使用方法,更重要的是提供了一个可扩展的测试框架。基于这个框架,我们可以: - 添加更多的舵机(如控制多关节机械臂) - 实现更复杂的运动轨迹(如S曲线加减速) - 与其他传感器联动(如根据光照调整舵机角度) 希望本文对您在嵌入式开发中有所帮助,欢迎在实际项目中应用和改进这个demo! --- **附录:完整代码获取** 本文的所有代码均已整合在 `hwtimer_demo.c` 文件中,可以通过FINSH命令直接运行测试。如有问题,欢迎交流讨论。