旋转时钟

准备制作一个旋转时钟,构思了一下,旋转时钟主要包括以下几个部分:

指针板、基板、电机、耦合线圈,用于电力的无线传输、无线串口,用于调试、显示和控制。


2013-10-10

至今天已经完成:

1.AVR单片机对时钟芯片DS1302的读写等操作

2.串口传输部分代码编写完成

准备完成:

1.红外遥控器编码的解码

2.程序匠人的程序原理深入了解,编写LED显示程序

3.购买旋转时钟配件,完成电源的无线传输电路设计与测试,LED板设计。

存在问题:

设置INT0中断为上升沿触发,每触发一次中断,将触发间隔的时间内累加计数传出来。但是用手触碰接收头时,串口会有数据传出。

项目进展:

1.DS1302时钟芯片的读写。

C语言文件:

#include "common.h"

/*-----------------------------------------------------------------
函数名称: void ds1302_init(void)
函数功能: ds1302总线初始化
参    数: 无
返 回 值: 无
-----------------------------------------------------------------*/
void ds1302_init(void)
{
 	RST_CLR;			 
	SCK_CLR;			 
	RST_OUT;			 
	SCK_OUT;
}
/*-----------------------------------------------------------------
函数名称: void ds1302_write_byte(unsigned int addr,unsigned int data)
函数功能: 向DS1302目标地址中写入一字节数据
参    数: 目标地址 一字节数据
返 回 值: 无
-----------------------------------------------------------------*/
void ds1302_write_byte(unsigned int addr,unsigned int data)
{
 	 unsigned int i;
	 RST_CLR;
	 SCK_CLR;
 	 RST_SET;//启动DS1302总线
	 //传输目标地址
	 IO_OUT;
	 addr&=0xfe;
	 for(i=0;i<8;i++)
	 {
	  if(addr&0x01)
	    {IO_SET;My_Putchar(0x31);}
	  else
	    {IO_CLR;My_Putchar(0x30);}
	 SCK_CLR;
	 SCK_SET;	 
	 addr=addr>>1;
	 }
	 //向目标地址中写入数据
	 IO_OUT;
	 My_Putstr("data:");
	 for(i=0;i<8;i++)
	 {
	  if(data&0x01)
	    {IO_SET;My_Putchar(0x31);}
		
	  else
	    {IO_CLR;My_Putchar(0x30);}
	 SCK_CLR;
	SCK_SET; 
	 data=data>>1;
	 }
	 RST_CLR;//禁止DS1302总线
}


/*-----------------------------------------------------------------
函数名称: int ds1302_read_byte(unsigned int addr)
函数功能: 从DS1302目标地址中读取一字节数据
参    数: 目标地址
返 回 值:  一字节数据
-----------------------------------------------------------------*/
unsigned int ds1302_read_byte(unsigned int addr)
{
 	unsigned int i,temp;
	temp=0x00;
	RST_CLR;
	SCK_CLR;
 	RST_SET;//启动DS1302总线
	//写入目标地址
 	IO_OUT;
 	addr=addr|0x01;
 	for(i=0;i<8;i++)
 	{
	 SCK_CLR;
 	 if(addr&0x01)
 	   IO_SET;
 	    else
 	   IO_CLR;
	 SCK_SET;
	 addr=addr>>1;
 	 }
	 //读取目标地址数据
	 IO_IN;
	 for(i=0;i<8;i++)
	 {	  
	  if(IO_R)
	   temp=temp|0x80;
	  else
	   temp=temp&0x7f;
	  SCK_SET;SCK_CLR;
	  temp=temp>>1;
	 }
	 RST_CLR;//禁止DS1302总线
	 return temp;
}

/*-----------------------------------------------------------------
函数名称: void ds1302_write_time(unsigned int *time_data)
函数功能: 将日期信息写入DS1302中
参    数: 数组中的时间信息
返 回 值: 无
-----------------------------------------------------------------*/
void ds1302_write_time(unsigned int *time_data)
{
    ds1302_write_byte(ds1302_control_addr,0x00);//解除写禁止;最高位WP清零
    ds1302_write_byte(ds1302_sec_addr,0x10);//暂停时钟;CH位置位
 	time_data++;
	ds1302_write_byte(ds1302_year_addr,*time_data++);	//只写入后面两位 08
	ds1302_write_byte(ds1302_month_addr,*time_data++);	//月 
	ds1302_write_byte(ds1302_date_addr,*time_data++);    //日  
	ds1302_write_byte(ds1302_hr_addr,*time_data++);		//时 
	ds1302_write_byte(ds1302_min_addr,*time_data++);		//分
	ds1302_write_byte(ds1302_sec_addr,*time_data++);		//秒
	ds1302_write_byte(ds1302_day_addr,*time_data);		//周 
	ds1302_write_byte(ds1302_control_addr,0x80);			//打开写保护 
}

/*-----------------------------------------------------------------
函数名称: void ds1302_read_time(unsigned char *time_data)
函数功能: 从DS1302中读取日期时间信息
参    数: 日期时间信息存放数组的地址
返 回 值: 无
-----------------------------------------------------------------*/
void ds1302_read_time(unsigned int *time_data)  
{ 
    time_data++;
	*time_data=ds1302_read_byte(ds1302_year_addr);	    //只读出后面两位08
	time_data++;
	*time_data=ds1302_read_byte(ds1302_month_addr);	    //月 
	time_data++;
	*time_data=ds1302_read_byte(ds1302_date_addr);		//日 
	time_data++;
	*time_data=ds1302_read_byte(ds1302_hr_addr);		    //时 
	time_data++;
	*time_data=ds1302_read_byte(ds1302_min_addr);	    //分 
	time_data++;
	*time_data=(ds1302_read_byte(ds1302_sec_addr))&0x7F; //秒 
	time_data++;
	*time_data=ds1302_read_byte(ds1302_day_addr);	    //周 
}
DS1302头文件:

#ifndef  _H_INCLUDE_
#define _H_INCLUDE_
//端口修改部分
#define DS1302_RST PA1
#define DS1302_IO  PA2
#define DS1302_SCLK PA3
#define DS1302_PORT PORTA
#define DS1302_DDR DDRA
#define DS1302_PIN PINA

//复位脚
#define RST_CLR	DS1302_PORT &= ~(1 << DS1302_RST)
#define RST_SET	DS1302_PORT |= (1 << DS1302_RST)
#define RST_IN	DS1302_DDR &= ~(1 << DS1302_RST)
#define RST_OUT	DS1302_DDR |= (1 << DS1302_RST)

//双向数据
#define IO_CLR	DS1302_PORT &= ~(1 << DS1302_IO)
#define IO_SET	DS1302_PORT |= (1 << DS1302_IO)
#define IO_R	DS1302_PIN & (1 << DS1302_IO)
#define IO_IN	DS1302_DDR &= ~(1 << DS1302_IO)
#define IO_OUT	DS1302_DDR |= (1 << DS1302_IO)

//时钟信号
#define SCK_CLR	DS1302_PORT &= ~(1 << DS1302_SCLK)
#define SCK_SET	DS1302_PORT |= (1 << DS1302_SCLK)
#define SCK_IN	DS1302_DDR &= ~(1 << DS1302_SCLK)
#define SCK_OUT	DS1302_DDR |= (1 << DS1302_SCLK)

#define ds1302_sec_addr			0x80		 
#define ds1302_min_addr			0x82		 
#define ds1302_hr_addr			0x84		 
#define ds1302_date_addr		0x86		 
#define ds1302_month_addr		0x88		 
#define ds1302_day_addr			0x8a		 
#define ds1302_year_addr		0x8c		 
#define ds1302_control_addr		0x8e		 
#define ds1302_charger_addr		0x90 					 
#define ds1302_clkburst_addr	0xbe

#endif



void ds1302_init(void);
void ds1302_write_byte(unsigned int addr,unsigned int data);
unsigned int ds1302_read_byte(unsigned int addr);
void ds1302_write_time(unsigned int *time_data);
void ds1302_read_time(unsigned int *time_data);

串口C文件

#include 
#include 
#define fosc 16000000
#define baud 9600
/*-----------------------------------------------------------------
函数名称: void Usart_init(unsigned int baud)
函数功能: 串口初始化
		  0.2%/8bit/1停止位/无奇偶校验
参    数: 波特率
返 回 值: 无
-----------------------------------------------------------------*/
void Usart_init(void)
{
  UBRRL=(fosc/16/(baud+1))%256;  /*波特率为:9600*/
  UBRRH=(fosc/16/(baud+1))/256;
  UCSRC=(1<

串口头文件

void Usart_init(void);
void Usart_Transmit(unsigned char i);
void My_Putstr(char *s);
void My_Putchar(unsigned char data );

2013-10-12

【问题1】昨天晚上红外接收管突然罢工了,对于遥控的呼唤爱理不理。我吧INT0接到按键,然后对案件计数,结果CPU对于按键的输入还是有响应的,可怜的IR3808,你肿么了,待我用示波器看你一看,不行的话就只能把你给换了。

言归正传,今天看了一上午代码,参看的大神程序匠人的程序,对大神深厚而规范的编程功底膜拜一下。

一个地方很不懂,他首先定义了一个结构体。

typedef struct  { 
 unsigned b0 : 1;      //结构元素表 
        unsigned b1 : 1; 
        unsigned b2 : 1; 
        unsigned b3 : 1; 
        unsigned b4 : 1; 
        unsigned b5 : 1; 
        unsigned b6 : 1; 
        unsigned b7 : 1; 
} BIT_F; 

然后定义了一个联合体,结构体BIT_F为其成员。

typedef union { 
  BIT_F   oneBit;   //按位寻址 
  tU08  allBits;  //按字节寻址 
}FLAG_union;      //定义一个既能按位寻址,也可按字节寻址的联合

在后续,匠人进行了一系列的宏定义:

EXT_ FLAG_union PORTA_TEMP;     //PORTA 口输出临时缓冲区 
EXT_ FLAG_union PORTB_TEMP;     //PORTB 口输出临时缓冲区 
EXT_ FLAG_union PORTC_TEMP;     //PORTC 口输出临时缓冲区 
EXT_ FLAG_union DISP_BUF;     //字码缓冲区 
#define PORTA_TMP PORTA_TEMP.allBits 
#define PORTB_TMP PORTB_TEMP.allBits 
#define PORTC_TMP PORTC_TEMP.allBits 
#define LED01_TMP PORTC_TEMP.oneBit.b4      //外框
#define LED02_TMP PORTB_TEMP.oneBit.b1      //行 1 
#define LED03_TMP PORTB_TEMP.oneBit.b2      //行 2 
#define LED04_TMP PORTB_TEMP.oneBit.b3      //行 3 
#define LED05_TMP PORTB_TEMP.oneBit.b4      //行 4 
#define LED06_TMP PORTB_TEMP.oneBit.b5      //行 5 
#define LED07_TMP PORTB_TEMP.oneBit.b6      //行 6 
#define LED08_TMP PORTB_TEMP.oneBit.b7      //行 7 
#define LED09_TMP PORTA_TEMP.oneBit.b1      //行 8 
#define LED10_TMP PORTA_TEMP.oneBit.b2      //行 9 
#define LED11_TMP PORTA_TEMP.oneBit.b3      //行 10 
#define LED12_TMP PORTA_TEMP.oneBit.b4      //行 11 
#define LED13_TMP PORTA_TEMP.oneBit.b5      //行 12 
#define LED14_TMP PORTC_TEMP.oneBit.b0      //行 13 
#define LED15_TMP PORTC_TEMP.oneBit.b1      //行 14 
#define LED16_TMP PORTC_TEMP.oneBit.b2      //行 15 
#define LED17_TMP PORTC_TEMP.oneBit.b3      //表针 
//(特别注意:该口与其他口的控制电平相反.其他口 0=亮,1=灭;该口 0=灭,1=亮) 
#define LED29_TMP PORTC_TEMP.oneBit.b5      //内框 
#define DISP_BF DISP_BUF.allBits 
#define DISP_BF_0 DISP_BUF.oneBit.b0      //bit_0 
#define DISP_BF_1 DISP_BUF.oneBit.b1      //bit_1 
#define DISP_BF_2 DISP_BUF.oneBit.b2      //bit_2 
#define DISP_BF_3 DISP_BUF.oneBit.b3      //bit_3 
#define DISP_BF_4 DISP_BUF.oneBit.b4      //bit_4 
#define DISP_BF_5 DISP_BUF.oneBit.b5      //bit_5 
#define DISP_BF_6 DISP_BUF.oneBit.b6      //bit_6 
#define DISP_BF_7 DISP_BUF.oneBit.b7      //bit_7 

程序匠人的基本思路我还是清楚,就是通过操作共用体中的成员来达到控制端口电平的作用,我疑惑的是,他只是简单的定义了一下共用体名称,并没有定义共用体成员对应的端口。比如说LED12_TMP对应的是A端口的4位,通过宏定义可以找到

LED12_TMP PORTA_TEMP.oneBit.b4
但是PORTA_TEMP.oneBit.b4只是作了如下定义:

EXT_ FLAG_union PORTA_TEMP;     //PORTA 口输出临时缓冲区 

并没有指定PORTA_TEMP就是对应的端口A。

【解答1】

估计是在某些头文件里面定义了,但是没有放在文档里.


【问题2】

IT工程师最大的特点就是有耐心,因为就算有脾气,程序也会让他没脾气。今天INT0端口突然不响应我的红外遥控的上升沿中断了,我将PD2(INT0)端口接到按键端口,发现能够触发,但是接到红外接收管端口就是不行?

【解决2】

IO初始化的时候,将PD2初始化为1,也就是输出,改为0就可以了。但是为什么能够响应按键中断却不能响应红外中断呢?PS:我真的毫无脾气了。


2013-10-14

今天终于把红外全部搞定了,至此串口、DS1302、红外三个大部分已经搞定,近段工作还有按键逻辑编程、LED显示界面程序编写。

首先还是来按键逻辑吧。

按键设置的基本操作逻辑如下:


旋转时钟_第1张图片
                                                                                          


2013-10-21

实现了红外解码、按键逻辑。剩下LED显示模块。


2013-10-31

今天试着考虑LED显示问题。

首先,希望通过宏定义来屏蔽端口差异化,底端LED端口通过类似sbit LED1 PB2类似定义来屏蔽端口差异化,然后通过类似数组形式来统一控制端口LED,如LED={LED1,LED2,......}。大体构想如此,但是如果端口控制电平并不归一将如何解决?如LED1高电平点亮LED,而LED2则是低电平点亮LED?那么在给LED端口统一赋值(假设控制高电平归一控制)之后,通过检测函数来修改异位电平。

然后就是LED点亮的问题了。借用匠人的图来说明吧。

旋转时钟_第2张图片


计划一周布置180列,关于LED布置其实我觉得不应该均衡布置,靠近中心的可以稀疏一点,而且外围LED控制可以更加精细一点,比如内部LED显示1ms,外围LED就可以显示0.5ms,以显示点比较匀称,不会造成看起来外部点大,内部点小的情况。好吧,先做一个基本版。

首先,在指针板上的过零中断中的计数器可以统计出旋转一周的数值,在数值稳定之后,则说明电机旋转稳定了,假设这个值为n。那么这一周上180列每一列占的时间为n/180,而LED点亮多久呢?这个需要具体调试。初步想法是先设定一个显示时间的基准值,让旋转时钟能够初步显示出来,然后用红外遥控器来调试,通过改变基准值来控制每一列点亮的时间,设置好之后再存放如eeprom中,然后后面就可以每次通过读取这个值来正常显示了。所以还需要设置一个调试模式了。

关于调试模式:还是通过红外。通过长按MODE键,进入调试模式,调试模式结束后,按EQ保存入EEPROM中,按取消取消设置。

当然关于调试模式的设定都是后面的事情了,现在这里MARK一下。

2013-11-13

最近有开题报告要弄,先中断一下,有点想法,先记下来。

【想法】有关旋转中断:

现有的方案是一周只有一个中断,底板放置一个小磁铁或者对管的发送端,这样做的弊端就是首先一周一次的终端使得显示控制不够精细,另外在机械安装方面,需要仔细的安装校对。我的想法是,红外的发送和接受端口均在指针板,在底板上贴上有黑白间隔或者白色部分间隔彩条的纸片,这样在一周之中,指针板能够检测到多次中断,这样有利于显示控制的精细,而且也不用精细的控制对管的位置。


2013-11-22

nice man!美好的一天!

开题报告搞完,可以动手做东西了。

计划:

周末用万用版搭建电路,并开始显示程序的着手调试。

显示端程序思路如下:

1.指针板旋转一周,定时器计数,设数值为counter_circle;

2.一周共180个显示列,每一列最长显示时间为counter_column=counter_circle/180;

3.实际每列显示时间为 display_column=counter_column-offset;


2013-11-24

程序匠人送显缓冲区程序分析:

1.定义联合体;2.端口位缓冲区赋值;3.按字节端口赋值;

1.定义联合体

typedef union { 
  BIT_F   oneBit;   //按位寻址 
  tU08  allBits;  //按字节寻址 
}FLAG_union;


2.端口位缓冲区赋值

//======== 
//IO 口输出临时缓冲区 
//======== 
EXT_ FLAG_union PORTA_TEMP;     //PORTA 口输出临时缓冲区 
EXT_ FLAG_union PORTB_TEMP;     //PORTB 口输出临时缓冲区 
EXT_ FLAG_union PORTC_TEMP;     //PORTC 口输出临时缓冲区 
EXT_ FLAG_union DISP_BUF;     //字码缓冲区 
#define PORTA_TMP PORTA_TEMP.allBits 
#define PORTB_TMP PORTB_TEMP.allBits 
#define PORTC_TMP PORTC_TEMP.allBits 
#define LED01_TMP PORTC_TEMP.oneBit.b4      //外框
#define LED02_TMP PORTB_TEMP.oneBit.b1      //行 1 
#define LED03_TMP PORTB_TEMP.oneBit.b2      //行 2 
#define LED04_TMP PORTB_TEMP.oneBit.b3      //行 3 
#define LED05_TMP PORTB_TEMP.oneBit.b4      //行 4 
#define LED06_TMP PORTB_TEMP.oneBit.b5      //行 5 
#define LED07_TMP PORTB_TEMP.oneBit.b6      //行 6 
#define LED08_TMP PORTB_TEMP.oneBit.b7      //行 7 
#define LED09_TMP PORTA_TEMP.oneBit.b1      //行 8 
#define LED10_TMP PORTA_TEMP.oneBit.b2      //行 9 
#define LED11_TMP PORTA_TEMP.oneBit.b3      //行 10 
#define LED12_TMP PORTA_TEMP.oneBit.b4      //行 11 
#define LED13_TMP PORTA_TEMP.oneBit.b5      //行 12 
#define LED14_TMP PORTC_TEMP.oneBit.b0      //行 13 
#define LED15_TMP PORTC_TEMP.oneBit.b1      //行 14 
#define LED16_TMP PORTC_TEMP.oneBit.b2      //行 15 
#define LED17_TMP PORTC_TEMP.oneBit.b3      //表针 
//(特别注意:该口与其他口的控制电平相反.其他口 0=亮,1=灭;该口 0=灭,1=亮) 
#define LED29_TMP PORTC_TEMP.oneBit.b5      //内框 
#define DISP_BF DISP_BUF.allBits 
#define DISP_BF_0 DISP_BUF.oneBit.b0      //bit_0 
#define DISP_BF_1 DISP_BUF.oneBit.b1      //bit_1 
#define DISP_BF_2 DISP_BUF.oneBit.b2      //bit_2 
#define DISP_BF_3 DISP_BUF.oneBit.b3      //bit_3 
#define DISP_BF_4 DISP_BUF.oneBit.b4      //bit_4 
#define DISP_BF_5 DISP_BUF.oneBit.b5      //bit_5 
#define DISP_BF_6 DISP_BUF.oneBit.b6      //bit_6 
#define DISP_BF_7 DISP_BUF.oneBit.b7      //bit_7 

3.按字节端口赋值

//======== 
//送显 
//======== 
PORTA = PORTA_TMP ;  
PORTB = PORTB_TMP ; 
PORTC = PORTC_TMP ; 

2013-11-26

程序匠人显示部分中断分析:

---- DISP_TIME_SET Matches (37 in 4 files) ----
Common.h:EXT_ tU16 DISP_TIME_SET;    //单列显示时间设置值(赋值给 TMR1H,TMR1L) 
Display.c:     DISP_QUEUE1[0] = DISP_TIME_SET / 10000 ;   
Display.c:     DISP_QUEUE1[1] = ( DISP_TIME_SET / 1000 ) % 10 ;    
Display.c:     DISP_QUEUE1[2] = ( DISP_TIME_SET / 100 ) % 10 ;  
Display.c:     DISP_QUEUE1[3] = ( DISP_TIME_SET / 10 ) % 10 ;   
Display.c:     DISP_QUEUE1[4] = DISP_TIME_SET % 10 ;   
Interruption.c:    TMR1HL = TMR1HL + DISP_TIME_SET ;  //TMR1 重新赋初值 
Interruption.c:   if ( TIMR1_JSQ > 220 ) DISP_TIME_SET = DISP_TIME_SET - 32 ; 
Interruption.c:   else if ( TIMR1_JSQ > 200 ) DISP_TIME_SET = DISP_TIME_SET - 16 ; 
Interruption.c:   else if ( TIMR1_JSQ > 190 ) DISP_TIME_SET = DISP_TIME_SET - 8 ; 
Interruption.c:   else if ( TIMR1_JSQ > 185 ) DISP_TIME_SET = DISP_TIME_SET - 4 ; 
Interruption.c:   else if ( TIMR1_JSQ > 182 ) DISP_TIME_SET = DISP_TIME_SET - 2 ; 
Interruption.c:   else DISP_TIME_SET = DISP_TIME_SET - 1 ; 
Interruption.c:   if ( DISP_TIME_SET < 400 ) DISP_TIME_SET = 400 ; //钳位 
Interruption.c:   if ( TIMR1_JSQ < 140 ) DISP_TIME_SET = DISP_TIME_SET + 32 ; 
Interruption.c:   else if ( TIMR1_JSQ < 160 ) DISP_TIME_SET = DISP_TIME_SET + 16 ; 
Interruption.c:   else if ( TIMR1_JSQ < 170 ) DISP_TIME_SET = DISP_TIME_SET + 8 ; 
Interruption.c:   else if ( TIMR1_JSQ < 175 ) DISP_TIME_SET = DISP_TIME_SET + 4 ; 
Interruption.c:   else if ( TIMR1_JSQ < 178 ) DISP_TIME_SET = DISP_TIME_SET + 2 ; 
Interruption.c:   else DISP_TIME_SET = DISP_TIME_SET + 1 ; 
Interruption.c:   if ( DISP_TIME_SET > 65100 ) DISP_TIME_SET = 65100 ; //钳位 
Main.c:  DISP_TIME_SET = (65536-1000) ;  //单列显示时间设置值 
Main.c:  TMR1HL = DISP_TIME_SET ;//TMR1 赋初值 
在源程序中搜索DISP_TIME_SET,便得到了匠人的思路。匠人将定时器设定值定义为:
TMR1HL = DISP_TIME_SET ;
随后在中断部分给出定时器1初始值赋值公式:
TMR1HL = TMR1HL + DISP_TIME_SET ;  //TMR1 重新赋初值

其中定时器溢出时间为(65535-TIMR1HL)/晶振频率。最后程序达到的效果为:每个旋转周期,定时器次数为180次。


2013-12-16

POV旋转时钟暂时初步成效:

旋转时钟_第3张图片

【存在问题】

1.转速平稳而高速时,溢出中断不能达到一圈180次速率。

2.显示部分为端口直接设定方法,不够模块化,输出英文字符串时,比较复杂。

【解决方法】

1.是否为熔丝位设定问题,贴片MEGA16默认为1M内部晶振

2.参考《51单片机POV趣味单片机制作详解》中显示方法:

(1)创建字符库数组ASCIIDOC[ ];

(2)通过引用向量数组V[ ]来引用ASCIIDOC[ ]字符,输出到端口;

  如:

PORTA=ASCIIDOC[V[i]*ZK_LS+j];

表示将ASCIIDOC中向量V[]中第i×ZK_LS+j个字符的取出来,其中ZK_LS为每个显示字符的列数,通过j来将每列逐个显示出来。












你可能感兴趣的:(旋转时钟)