|
步進(jìn)電機(jī)用的是5V的28BYJ48,是4相5線的,驅(qū)動板是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;
// 定義步進(jìn)電機(jī)IO口,假設(shè)連接到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; // 當(dāng)前高度角
float current_azimuth = 0.0; // 當(dāng)前方位角
unsigned int auto_update_counter = 0; // 自動模式更新計(jì)數(shù)器
// 經(jīng)緯度 (需要根據(jù)實(shí)際情況修改)
float latitude = 39.9; // 北京的緯度
float longitude = 116.3; // 北京的經(jīng)度
// DS1302 時(shí)鐘相關(guān)
unsigned char time[7] = {0}; // 秒,分,時(shí),日,月,星期,年
// 步進(jìn)電機(jī)相關(guān)
// unsigned char motor_step_table[] = {0x08, 0x0C, 0x04, 0x06, 0x02, 0x03, 0x01, 0x09}; // 步進(jìn)電機(jī)步序,原始步序
unsigned char motor_step_table[] = {0x09, 0x01, 0x03, 0x02, 0x06, 0x04, 0x0c, 0x08}; // 步進(jìn)電機(jī)步序, 嘗試的步序
int altitude_motor_step = 0; // 高度角電機(jī)當(dāng)前步數(shù)
int azimuth_motor_step = 0; // 方位角電機(jī)當(dāng)前步數(shù)
#define MOTOR_STEP_ANGLE 1.8 // 步進(jìn)電機(jī)每步轉(zhuǎn)動的角度 (根據(jù)實(shí)際情況修改)
// 軟件限位 (根據(jù)實(shí)際情況修改)
#define MAX_ALTITUDE_STEPS 100 // 高度角最大步數(shù), 對應(yīng)最大角度 180 度
#define MAX_AZIMUTH_STEPS 200 // 方位角最大步數(shù), 對應(yīng)最大角度 360 度
// 函數(shù)聲明
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();
// 延時(shí)函數(shù)
void delay(unsigned int i) {
while (i--);
}
void delay_us(unsigned int i) {
while (i--) {
_nop_();
_nop_();
_nop_();
_nop_();
}
}
// LCD12864 相關(guān)函數(shù)
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); // 顯示開,光標(biāo)關(guān),閃爍關(guān)
delay_us(100);
lcd_write_command(0x01); // 清除顯示
delay_us(10000);
lcd_write_command(0x06); // 光標(biāo)右移,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 相關(guān)函數(shù)
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); // 突發(fā)模式寫
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); // 突發(fā)模式讀
for (i = 0; i < 7; i++) {
time[i] = ds1302_read_byte();
}
DS1302_RST = 0;
}
// 太陽位置計(jì)算 (時(shí)控法)
void calculate_sun_position(float *altitude, float *azimuth) {
// 將時(shí)間轉(zhuǎn)換為小時(shí) (UTC 時(shí)間)
float hour = time[2] + time[1] / 60.0 + time[0] / 3600.0 - 8.0; // 轉(zhuǎn)換為UTC時(shí)間
// 把所有變量聲明放在函數(shù)開頭
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;
// 計(jì)算儒略日 (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;
// 計(jì)算儒略世紀(jì)數(shù) (T)
T = (JD - 2451545.0) / 36525.0;
// 計(jì)算太陽的平黃經(jīng) (L)
L = 280.46646 + 36000.76983 * T + 0.0003032 * T * T;
L = fmod(L, 360.0); // 取模
// 計(jì)算太陽的平近點(diǎn)角 (g)
g = 357.52911 + 35999.05029 * T - 0.0001537 * T * T;
g = fmod(g, 360.0);
// 計(jì)算太陽的黃經(jīng) (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);
// 計(jì)算黃赤交角 (epsilon)
epsilon = 23.439291 - 0.0130042 * T - 0.00000016 * T * T;
// 計(jì)算太陽的赤緯 (delta)
delta = asin(sin(epsilon * 3.14159265 / 180.0) * sin(lambda * 3.14159265 / 180.0)) * 180.0 / 3.14159265;
// 計(jì)算時(shí)角 (H)
H = (hour * 15.0) - 180.0 - longitude;
H = fmod(H, 360.0);
// 計(jì)算高度角 (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;
// 計(jì)算方位角 (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;
}
}
// 步進(jìn)電機(jī)控制 (簡化版, 用于調(diào)試)
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) { // 高度角電機(jī)
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 (假設(shè) 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); // 更長的延時(shí), 便于觀察
} else { // 方位角電機(jī)
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 (假設(shè) 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); //更長的延時(shí), 便于觀察
}
}
}
// 更新電機(jī)位置
void update_motor_position() {
int altitude_steps, azimuth_steps;
float target_altitude, target_azimuth;
if (mode == 0) { // 自動模式
calculate_sun_position(&target_altitude, &target_azimuth);
// 計(jì)算需要轉(zhuǎn)動的步數(shù)
altitude_steps = (int)((target_altitude - current_altitude) / MOTOR_STEP_ANGLE);
azimuth_steps = (int)((target_azimuth - current_azimuth) / MOTOR_STEP_ANGLE);
// 更新當(dāng)前角度
current_altitude += altitude_steps * MOTOR_STEP_ANGLE;
current_azimuth += azimuth_steps * MOTOR_STEP_ANGLE;
// 驅(qū)動電機(jī)
motor_rotate(0, altitude_steps); // 高度角電機(jī)
motor_rotate(1, azimuth_steps); // 方位角電機(jī)
}
}
// 按鈕掃描
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; // 無按鈕按下
}
// 重新實(shí)現(xiàn) _putchar 函數(shù),輸出到 LCD (注意是 _putchar)
char _putchar(char c) {
lcd_write_data(c); // 使用你的 LCD 數(shù)據(jù)寫入函數(shù)
return c;
}
void main() {
unsigned char btn_value;
unsigned char display_str[17];
float displayed_altitude, displayed_azimuth; // 用于顯示的限幅后的角度
lcd_init();
lcd_clear();
// 設(shè)置初始時(shí)間 (根據(jù)實(shí)際情況修改)
time[0] = 0x00; // 秒
time[1] = 0x56; // 分
time[2] = 0x17; // 時(shí)
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(); // 讀取時(shí)間
btn_value = button_scan(); // 掃描按鈕
if (btn_value == 1) { // 模式切換
mode = !mode;
}
if (mode == 0) { // 自動模式
lcd_display_string(0, 0, "Auto Mode ");
// update_motor_position(); // 先注釋掉原來的調(diào)用
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; // 負(fù)數(shù)轉(zhuǎn)正數(shù)
}
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;
// 顯示當(dāng)前角度 (使用限幅后的值)
sprintf(display_str, "A:%.1f E:%.1f", displayed_azimuth, displayed_altitude);
lcd_display_string(0, 1, display_str);
delay(100); // 延時(shí)
}
|
|