找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 535|回复: 11
打印 上一主题 下一主题
收起左侧

我这个程序的电机只会震动不会转,是哪里出了问题啊。

[复制链接]
跳转到指定楼层
楼主
步进电机用的是5V的28BYJ48,是4相5线的,驱动板是ULN2003。
#include <STC12C5A60S2.H>
#include <intrins.h>
#include <math.h>
#include <stdio.h>

// 定义IO口
sbit LCD_RS = P1^0;
sbit LCD_RW = P1^1;
sbit LCD_EN = P1^2;
sbit LCD_D0 = P2^0;
sbit LCD_D1 = P2^1;
sbit LCD_D2 = P2^2;
sbit LCD_D3 = P2^3;
sbit LCD_D4 = P2^4;
sbit LCD_D5 = P2^5;
sbit LCD_D6 = P2^6;
sbit LCD_D7 = P2^7;

sbit DS1302_CLK = P3^4;
sbit DS1302_IO  = P3^5;
sbit DS1302_RST = P3^6;

sbit BTN_MODE = P3^0;
sbit BTN_UP   = P3^1;
sbit BTN_DOWN = P3^2;
sbit BTN_LEFT = P3^3;
sbit BTN_RIGHT= P3^7;

// 定义步进电机IO口,假设连接到P0口
sbit IN1 = P0^0;
sbit IN2 = P0^1;
sbit IN3 = P0^2;
sbit IN4 = P0^3;
sbit IN5 = P0^4;
sbit IN6 = P0^5;
sbit IN7 = P0^6;
sbit IN8 = P0^7;

// 全局变量
unsigned char mode = 0; // 0: 自动模式, 1: 手动模式
float current_altitude = 0.0; // 当前高度角
float current_azimuth = 0.0;  // 当前方位角
unsigned int auto_update_counter = 0; // 自动模式更新计数器


// 经纬度 (需要根据实际情况修改)
float latitude = 39.9;  // 北京的纬度
float longitude = 116.3; // 北京的经度

// DS1302 时钟相关
unsigned char time[7] = {0}; // 秒,分,时,日,月,星期,年

// 步进电机相关
// unsigned char motor_step_table[] = {0x08, 0x0C, 0x04, 0x06, 0x02, 0x03, 0x01, 0x09}; // 步进电机步序,原始步序
unsigned char motor_step_table[] = {0x09, 0x01, 0x03, 0x02, 0x06, 0x04, 0x0c, 0x08}; // 步进电机步序, 尝试的步序
int altitude_motor_step = 0;  // 高度角电机当前步数
int azimuth_motor_step = 0;   // 方位角电机当前步数
#define MOTOR_STEP_ANGLE 1.8 // 步进电机每步转动的角度 (根据实际情况修改)

// 软件限位 (根据实际情况修改)
#define MAX_ALTITUDE_STEPS 100 // 高度角最大步数, 对应最大角度 180 度
#define MAX_AZIMUTH_STEPS 200  // 方位角最大步数, 对应最大角度 360 度

// 函数声明
void delay(unsigned int i);
void delay_us(unsigned int i);
void lcd_write_command(unsigned char command);
void lcd_write_data(unsigned char dat);
void lcd_init();
void lcd_clear();
void lcd_display_string(unsigned char x, unsigned char y, unsigned char *str);
void ds1302_write_byte(unsigned char dat);
unsigned char ds1302_read_byte();
void ds1302_write_time();
void ds1302_read_time();
void calculate_sun_position(float *altitude, float *azimuth);
void motor_rotate(unsigned char motor, int steps);
void update_motor_position();
unsigned char button_scan();

// 延时函数
void delay(unsigned int i) {
    while (i--);
}

void delay_us(unsigned int i) {
    while (i--) {
        _nop_();
        _nop_();
        _nop_();
        _nop_();
    }
}

// LCD12864 相关函数
void lcd_write_command(unsigned char command) {
    LCD_RS = 0;
    LCD_RW = 0;
    P2 = command;
    LCD_EN = 1;
    delay_us(5);
    LCD_EN = 0;
}

void lcd_write_data(unsigned char dat) {
    LCD_RS = 1;
    LCD_RW = 0;
    P2 = dat;
    LCD_EN = 1;
    delay_us(5);
    LCD_EN = 0;
}

void lcd_init() {
    lcd_write_command(0x30); // 基本指令集
    delay_us(100);
    lcd_write_command(0x30); // 基本指令集
    delay_us(37);
    lcd_write_command(0x0C); // 显示开,光标关,闪烁关
    delay_us(100);
    lcd_write_command(0x01); // 清除显示
    delay_us(10000);
    lcd_write_command(0x06); // 光标右移,AC+1
    delay_us(100);
}

void lcd_clear() {
    lcd_write_command(0x01); // 清除显示
    delay(2);
}

void lcd_display_string(unsigned char x, unsigned char y, unsigned char *str) {
    if (y == 0) {
        lcd_write_command(0x80 + x); // 第一行
    } else {
        lcd_write_command(0x90 + x); // 第二行
    }
    while (*str) {
        lcd_write_data(*str++);
    }
}

// DS1302 相关函数
void ds1302_write_byte(unsigned char dat) {
    unsigned char i;
    for (i = 0; i < 8; i++) {
        DS1302_CLK = 0;
        DS1302_IO = dat & 0x01;
        DS1302_CLK = 1;
        dat >>= 1;
    }
}

unsigned char ds1302_read_byte() {
    unsigned char i, dat = 0;
    for (i = 0; i < 8; i++) {
        DS1302_CLK = 0;
        dat >>= 1;
        if (DS1302_IO) {
            dat |= 0x80;
        }
        DS1302_CLK = 1;
    }
    return dat;
}

void ds1302_write_time() {
    unsigned char i;
    DS1302_RST = 0;
    DS1302_CLK = 0;
    DS1302_RST = 1;
    ds1302_write_byte(0x8E); // 控制命令, WP=0, 允许写
    ds1302_write_byte(0x00); // 允许写
    DS1302_RST = 0;

    DS1302_RST = 1;
    ds1302_write_byte(0xBE); // 突发模式写
    for (i = 0; i < 7; i++) {
        ds1302_write_byte(time[i]);
    }
    DS1302_RST = 0;
}

void ds1302_read_time() {
    unsigned char i;
    DS1302_RST = 0;
    DS1302_CLK = 0;
    DS1302_RST = 1;
    ds1302_write_byte(0xBF); // 突发模式读
    for (i = 0; i < 7; i++) {
        time[i] = ds1302_read_byte();
    }
    DS1302_RST = 0;
}

// 太阳位置计算 (时控法)
void calculate_sun_position(float *altitude, float *azimuth) {
    // 将时间转换为小时 (UTC 时间)
    float hour = time[2] + time[1] / 60.0 + time[0] / 3600.0 - 8.0; // 转换为UTC时间
    // 把所有变量声明放在函数开头
    int year, month, day;
    int A, B;
    long JD;
    float T, L, g, lambda, epsilon, delta, H, cos_azimuth;

    if (hour < 0) hour += 24.0;

    // 计算儒略日 (JD)
    year = 2000 + time[6]; // 年份
    month = time[4];       // 月份
    day = time[3];         // 日

    if (month <= 2) {
        year--;
        month += 12;
    }
    A = year / 100;
    B = 2 - A + A / 4;
    JD = (long)(365.25 * (year + 4716)) + (long)(30.6001 * (month + 1)) + day + B - 1524.5;

    // 计算儒略世纪数 (T)
    T = (JD - 2451545.0) / 36525.0;

    // 计算太阳的平黄经 (L)
    L = 280.46646 + 36000.76983 * T + 0.0003032 * T * T;
    L = fmod(L, 360.0); // 取模

    // 计算太阳的平近点角 (g)
    g = 357.52911 + 35999.05029 * T - 0.0001537 * T * T;
    g = fmod(g, 360.0);

    // 计算太阳的黄经 (lambda)
    lambda = L + 1.914602 * sin(g * 3.14159265 / 180.0) - 0.004817 * sin(2 * g * 3.14159265 / 180.0) - 0.019993 * T;
    lambda = fmod(lambda, 360.0);

    // 计算黄赤交角 (epsilon)
    epsilon = 23.439291 - 0.0130042 * T - 0.00000016 * T * T;

    // 计算太阳的赤纬 (delta)
    delta = asin(sin(epsilon * 3.14159265 / 180.0) * sin(lambda * 3.14159265 / 180.0)) * 180.0 / 3.14159265;

    // 计算时角 (H)
    H = (hour * 15.0) - 180.0 - longitude;
    H = fmod(H, 360.0);

    // 计算高度角 (altitude)
    *altitude = asin(sin(latitude * 3.14159265 / 180.0) * sin(delta * 3.14159265 / 180.0) +
                     cos(latitude * 3.14159265 / 180.0) * cos(delta * 3.14159265 / 180.0) * cos(H * 3.14159265 / 180.0)) * 180.0 / 3.14159265;

    // 计算方位角 (azimuth)
    cos_azimuth = (sin(delta * 3.14159265 / 180.0) - sin(*altitude * 3.14159265 / 180.0) * sin(latitude * 3.14159265 / 180.0)) /
                        (cos(*altitude * 3.14159265 / 180.0) * cos(latitude * 3.14159265 / 180.0));
    if (cos_azimuth > 1.0) cos_azimuth = 1.0;
    if (cos_azimuth < -1.0) cos_azimuth = -1.0;
    *azimuth = acos(cos_azimuth) * 180.0 / 3.14159265;

    if (H > 0) {
        *azimuth = 360.0 - *azimuth;
    }
}

// 步进电机控制 (简化版, 用于调试)
void motor_rotate(unsigned char motor, int steps) {
    int i, direction;
    unsigned char motor_output;

    direction = (steps > 0) ? 1 : -1;
    steps = (steps > 0) ? steps : -steps;

    for (i = 0; i < steps; i++) {
        if (motor == 0) { // 高度角电机
            altitude_motor_step += direction;
            if (altitude_motor_step < 0) altitude_motor_step = 0;
            if (altitude_motor_step > MAX_ALTITUDE_STEPS) altitude_motor_step = MAX_ALTITUDE_STEPS;

            // 直接控制 IO (假设 A-AB-B-BC-C-CD-D-DA 相序)
            if (altitude_motor_step % 8 == 0) P0 = (P0 & 0xF0) | 0x08; // A
            if (altitude_motor_step % 8 == 1) P0 = (P0 & 0xF0) | 0x0C; // AB
            if (altitude_motor_step % 8 == 2) P0 = (P0 & 0xF0) | 0x04; // B
            if (altitude_motor_step % 8 == 3) P0 = (P0 & 0xF0) | 0x06; // BC
            if (altitude_motor_step % 8 == 4) P0 = (P0 & 0xF0) | 0x02; // C
            if (altitude_motor_step % 8 == 5) P0 = (P0 & 0xF0) | 0x03; // CD
            if (altitude_motor_step % 8 == 6) P0 = (P0 & 0xF0) | 0x01; // D
            if (altitude_motor_step % 8 == 7) P0 = (P0 & 0xF0) | 0x09; // DA

            delay(50); // 更长的延时, 便于观察
        } else { // 方位角电机
            azimuth_motor_step += direction;
            if (azimuth_motor_step < 0) azimuth_motor_step = 0;
            if (azimuth_motor_step > MAX_AZIMUTH_STEPS) azimuth_motor_step = MAX_AZIMUTH_STEPS;

            // 直接控制 IO (假设 A-AB-B-BC-C-CD-D-DA 相序)
            if (azimuth_motor_step % 8 == 0) P0 = (P0 & 0x0F) | 0x90; // A
            if (azimuth_motor_step % 8 == 1) P0 = (P0 & 0x0F) | 0x10; // AB
            if (azimuth_motor_step % 8 == 2) P0 = (P0 & 0x0F) | 0x30; // B
            if (azimuth_motor_step % 8 == 3) P0 = (P0 & 0x0F) | 0x20; // BC
            if (azimuth_motor_step % 8 == 4) P0 = (P0 & 0x0F) | 0x60; // C
            if (azimuth_motor_step % 8 == 5) P0 = (P0 & 0x0F) | 0x40; // CD
            if (azimuth_motor_step % 8 == 6) P0 = (P0 & 0x0F) | 0xC0; // D
            if (azimuth_motor_step % 8 == 7) P0 = (P0 & 0x0F) | 0x80; // DA

            delay(50); //更长的延时, 便于观察
        }
    }
}

// 更新电机位置
void update_motor_position() {
    int altitude_steps, azimuth_steps;
    float target_altitude, target_azimuth;

    if (mode == 0) { // 自动模式
        calculate_sun_position(&target_altitude, &target_azimuth);

        // 计算需要转动的步数
        altitude_steps = (int)((target_altitude - current_altitude) / MOTOR_STEP_ANGLE);
        azimuth_steps = (int)((target_azimuth - current_azimuth) / MOTOR_STEP_ANGLE);

        // 更新当前角度
        current_altitude += altitude_steps * MOTOR_STEP_ANGLE;
        current_azimuth += azimuth_steps * MOTOR_STEP_ANGLE;

        // 驱动电机
        motor_rotate(0, altitude_steps); // 高度角电机
        motor_rotate(1, azimuth_steps); // 方位角电机
    }
}

// 按钮扫描
unsigned char button_scan() {
    if (!BTN_MODE) {
        delay(20); // 消抖
        if (!BTN_MODE) {
            while (!BTN_MODE); // 等待释放
            return 1; // 模式切换
        }
    }
    if (!BTN_UP) {
        delay(20);
        if (!BTN_UP) {
            while (!BTN_UP);
            return 2; // 上
        }
    }
    if (!BTN_DOWN) {
        delay(20);
        if (!BTN_DOWN) {
            while (!BTN_DOWN);
            return 3; // 下
        }
    }
    if (!BTN_LEFT) {
        delay(20);
        if (!BTN_LEFT) {
            while (!BTN_LEFT);
            return 4; // 左
        }
    }
    if (!BTN_RIGHT) {
        delay(20);
        if (!BTN_RIGHT) {
            while (!BTN_RIGHT);
            return 5; // 右
        }
    }
    return 0; // 无按钮按下
}

// 重新实现 _putchar 函数,输出到 LCD (注意是 _putchar)
char _putchar(char c) {
    lcd_write_data(c); // 使用你的 LCD 数据写入函数
    return c;
}

void main() {
    unsigned char btn_value;
    unsigned char display_str[17];
    float displayed_altitude, displayed_azimuth; // 用于显示的限幅后的角度

    lcd_init();
    lcd_clear();

    // 设置初始时间 (根据实际情况修改)
    time[0] = 0x00; // 秒
    time[1] = 0x56; // 分
    time[2] = 0x17; // 时
    time[3] = 0x26; // 日
    time[4] = 0x09; // 月
    time[5] = 0x03; // 星期
    time[6] = 0x23; // 年
    ds1302_write_time();

    current_altitude = 0;
    current_azimuth = 0;

    while (1) {
        ds1302_read_time(); // 读取时间

        btn_value = button_scan(); // 扫描按钮

        if (btn_value == 1) { // 模式切换
            mode = !mode;
        }

        if (mode == 0) { // 自动模式
            lcd_display_string(0, 0, "Auto Mode       ");
            // update_motor_position(); // 先注释掉原来的调用

            auto_update_counter++;
            if (auto_update_counter >= 300) { // 30 秒
                update_motor_position();
                auto_update_counter = 0; // 清零
            }
        }
         else { // 手动模式
            lcd_display_string(0, 0, "Manual Mode     ");
            if (btn_value == 2) { // 上
                motor_rotate(0, 1); // 每次只走一步
                current_altitude += MOTOR_STEP_ANGLE; //每次只增加一步
                if (current_altitude < 0) current_altitude = 0; // 限制下限
            }
            if (btn_value == 3) { // 下
                motor_rotate(0, -1); //每次只走一步
                current_altitude -= MOTOR_STEP_ANGLE; //每次只减少一步
                if (current_altitude > MAX_ALTITUDE_STEPS * MOTOR_STEP_ANGLE) current_altitude = MAX_ALTITUDE_STEPS * MOTOR_STEP_ANGLE; //限制上限
            }
            if (btn_value == 4) { // 左
                motor_rotate(1, -1); //每次只走一步
                current_azimuth -= MOTOR_STEP_ANGLE; //每次只减少一步
                current_azimuth = fmod(current_azimuth, 360.0); // 限制在 0-360 度
                if (current_azimuth < 0) current_azimuth += 360.0; // 负数转正数

            }
            if (btn_value == 5) { // 右
                motor_rotate(1, 1); //每次只走一步
                current_azimuth += MOTOR_STEP_ANGLE;  //每次只增加一步
                current_azimuth = fmod(current_azimuth, 360.0); // 限制在 0-360 度
            }
        }

        // 限幅处理, 用于显示
        displayed_altitude = current_altitude;
        displayed_azimuth = current_azimuth;

        if (displayed_altitude < 0) displayed_altitude = 0;
        if (displayed_altitude > MAX_ALTITUDE_STEPS * MOTOR_STEP_ANGLE) displayed_altitude = MAX_ALTITUDE_STEPS * MOTOR_STEP_ANGLE;

        // 显示当前角度 (使用限幅后的值)
        sprintf(display_str, "A:%.1f E:%.1f", displayed_azimuth, displayed_altitude);
        lcd_display_string(0, 1, display_str);

        delay(100); // 延时
    }


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

12#
ID:1145974 发表于 2025-4-9 20:32 | 只看该作者
找朋友帮忙看了,发现是程序的问题,现在的ai还是比较弱的,写程序还是一般,只能保证能编译但是不能完全实现功能。现在已经搞定问题了,需要的功能都能实现了,谢谢大家的帮助了。
回复

使用道具 举报

11#
ID:536683 发表于 2025-3-31 11:59 | 只看该作者
查一下硬件,线序接错了,也会只震动,不转。
回复

使用道具 举报

10#
ID:69038 发表于 2025-3-30 10:17 | 只看该作者
会震动不会转,要么时序出错,要么丢步。
回复

使用道具 举报

9#
ID:102168 发表于 2025-3-29 23:16 | 只看该作者
HAOHAO567 发表于 2025-3-29 16:02
但是他驱动板上的ABCD的相序灯是能轮着亮的,我也试过5v电池单独接一个电机,他也是不转,只会震动,我现在 ...

你用的这个步进电机带减速齿轮,速度转起来估计也不会太快,
你能看到相序灯切换,说明你给的脉冲频率很低了,再加上减速齿轮的作用,最后输出轴转得就更慢了。
你可以在输出轴上粘贴个长点的纸片什么的,这样有转动了也看得明显一些,多转一会儿看看变化。
回复

使用道具 举报

8#
ID:1145974 发表于 2025-3-29 16:02 | 只看该作者
但是他驱动板上的ABCD的相序灯是能轮着亮的,我也试过5v电池单独接一个电机,他也是不转,只会震动,我现在买了另外一家店的电机,看看是不是电机的问题。

51hei图片_20250329160012.jpg (357.65 KB, 下载次数: 0)

51hei图片_20250329160012.jpg
回复

使用道具 举报

7#
ID:1145974 发表于 2025-3-29 15:59 | 只看该作者
chxelc 发表于 2025-3-29 14:09
把延时时间改长或者改短看看。delay(50)那里。

我程序的延时(50改成2和5)和电机的延时(100改成50)我都改了,也不行.我打算换一家店的电机了。
回复

使用道具 举报

6#
ID:517466 发表于 2025-3-29 15:07 | 只看该作者
建议你按照如下步骤调整程序
1、处理注释掉主程序中初始化以外的所有处理
2、将步进电机的正反向驱动写成函数形式
3、在主程序中先测试电机的正反转,看成不成功
4、第三步成功了以后,再去把时钟部分和步进电机的部分整合起来。

单独函数化及测试步进电机的驱动,应该不麻烦。电机的正反转函数,按照单步方式书写。多步函数,按照步数调用单步函数。整个程序尽可能都函数化编程,这样逻辑关系清晰。
回复

使用道具 举报

5#
ID:712097 发表于 2025-3-29 14:09 | 只看该作者
把延时时间改长或者改短看看。delay(50)那里。
回复

使用道具 举报

地板
ID:469589 发表于 2025-3-29 13:13 | 只看该作者
一般启动速度太低或太高都会有这种现象,供参考
回复

使用道具 举报

板凳
ID:469589 发表于 2025-3-29 12:40 | 只看该作者
启动速度太快了,慢慢的启动
回复

使用道具 举报

沙发
ID:1145974 发表于 2025-3-29 08:01 | 只看该作者
一直不成功,就只有电机不转的问题了,我真的搞不明白,各位大哥帮帮我
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表