C语言实现电子时钟

来源:互联网 发布:网络信息安全博览会 编辑:程序博客网 时间:2024/05/16 11:01

利用液晶屏显示一个电子时钟是一个初学LCD时不错的练习项目。

我的这个LCD时钟小项目是基于S3C2440A的开发板的,如果想要全部的代码,可以去下载同名的资源。由于篇幅有限,以下只介绍核心代码。

好了开始之前先简单介绍下我的LCD显示原理。

我所采用的LCD显示原理是将显存作为一个二维数组,将该数组的起始地址告诉控制LCD的寄存器,数组中的每一个元素都是一个16BPP的像素。

首先,我们要有一个画点函数如下:

/**************************************************************************
***** 函数名:  PutPixel(void)
***** 功能: 绘制像素点
***** 参数:    x:横坐标 y:纵坐标 c颜色
***** 返回值: 无
***** 创建者: 
***** 创建时间: 2011-03-30
***** 最后更新:2011-03-30 
****************************************************************************/
void PutPixel(U32 x,U32 y, U32 c )
{
 LCD_BUFFER[y][x] = c;
}

然后我们还需要一个画圆的函数

在此有两种推荐:第一种速度快,第二种算法比较好理解。个人推测是因为第二种算法引入了浮点运算且判断条件较多的原因,即便加入了内存管理即MMU,速度也不是很理想,如果画的点多的话,人眼还是能够看到画圆的过程。

第一种:

/**************************************************************************
***** 函数名:   MidpointCircle(int x0,int y0,int r,int color)***** 功能:画个圆
***** 参数:     圆心x,y坐标,半径,颜色
***** 返回值:   无
***** 创建者: 

***** 创建时间: 2012-12-23
***** 最后更新:  2012-12-23
****************************************************************************/
void MidpointCircle(int x0,int y0,int r,int color)
{
 int x,y;
 float d;
 x=0;
 y=r;
 d=5.0/4-r;
 while(x<=y)
 {
  PutPixel(x0+x,y0+y,color);
  PutPixel(x0+x,y0-y,color);
  PutPixel(x0-x,y0+y,color);
  PutPixel(x0-x,y0-y,color);
  PutPixel(x0+y,y0+x,color);
  PutPixel(x0+y,y0-x,color);
  PutPixel(x0-y,y0+x,color);
  PutPixel(x0-y,y0-x,color);
  if(d<0)
  {
   d+=x*2.0+3;
  }

  else
  {
   d+=2.0*(x-y)+5;
   y--;
  }
  x++;
 }
}

第二种:

/**************************************************************************
***** 函数名:  draw_circle(unsigned int x1, unsigned int y1,
          unsigned int radius,unsigned int color)
***** 功能:    画一个圆
***** 参数:    已知点坐标x1,y1;半径radius,颜色color;
***** 返回值:  无
***** 创建者:   潘星宇
***** 创建时间:2012-12-30
***** 最后更新: 2012-12-30
****************************************************************************/
void draw_circle(unsigned int x1,    //圆心横坐标
     unsigned int y1,   //圆心纵坐标
     unsigned int radius,  //半径
     unsigned int color)  //颜色
{
 unsigned int i;
 for (i=0; i <= 359; i++)
 {
     one_point_to_another (x1, y1, radius, i, color, 0, 1);
 }
}
第二种大家一定会好奇里面的那个函数是什么,这就是我要告诉大家的电子钟的最关键的一个函数:

/**************************************************************************
***** 函数名:  one_point_to_another(unsigned int x1, unsigned int y1,
             unsigned int length, double angle,
          unsigned int color, unsigned int length_start,
          unsigned int length_end)
***** 功能:    由一点,长度和角度做线
***** 参数:    已知点坐标x1,y1;长度length,角度angle,颜色color;起始点和结束点;
    角度按照钟表原则,0点为0°,顺时针旋转,
    函数内部将角度按照象限的原则转化,第一象限为0°的开始。
    length_start_points:希望保留起始的点的个数
    length_end_points:希望保留最后的点的个数
***** 返回值:  无
***** 创建者:   潘星宇
***** 创建时间:2012-12-25
***** 最后更新: 2012-12-28
****************************************************************************/
void one_point_to_another(unsigned int x1,         //已知点横坐标x1
        unsigned int y1,        //已知点纵坐标y1
           unsigned int length,        //两点间距长度
        float angle,          //两点角度,按照钟表原则,0点为0°,顺时针旋转
           unsigned int color,        //颜色
        unsigned int length_start_points, //希望保留起始的点的个数
           unsigned int length_end_points) //希望保留最后的点的个数
{
 unsigned int x2;       //画线用横坐标寄存器
 unsigned int y2;       //画线用纵坐标寄存器
 unsigned int length_i;      //画线计数用
 unsigned int length_end_i = 0; //画线取最后几个点计数用
 float radian;     //弧度
    if ((angle >= 0) && (angle <= 90))
    {
     angle = 90 - angle;
    }
    else if ((angle >= 91) && (angle <= 180))
    {
  angle = 360 - (angle - 90);
    }
    else if ((angle >= 181) && (angle <=270))
    {
  angle = 270 - (angle - 180);
    }
    else if ((angle >= 271) && (angle <=359))
    {
  angle = 180 - (angle - 270);
    }
 
 radian = (angle / 180)*PIN;    //角度与弧度的转换
 
 if (length_start_points >= length)  //如果希望保留前面的点超出了线的长度
 {
  length_start_points = length; 
 }

 if (length_end_points >= length)      //如果希望保留后面的点超出了线的长度
 {
  length_end_points = length; 
 }

 for (length_i = 0; length_i <= length; length_i++)
 {
  
  if ((length_start_points > 0) || (length_end_i >= (length - length_end_points))) //希望保留起始点的个数
  {
   x2 = (unsigned int)(x1 + length_i*cos(radian) + 0.5); //加0.5为了防止大于5的小数被舍去
      y2 = (unsigned int)(y1 - length_i*sin(radian) + 0.5); //由于液晶屏的坐标Y轴向下坐标大,与象限刚好相反,故在此是减去
   PutPixel(x2,y2,color);
   if (length_start_points > 0) //防止自减运算溢出,变成最大值
   {
    length_start_points--;
   }
  }
     length_end_i++;
 }
}

在此补充两点:

(1)大家应该知道数学上的角度,是以第一象限X轴正半轴为起点,逆时针旋转360度,而时钟则是以Y轴正半轴为起点顺时针旋转的。

(2)PIN就是圆周率3.1415923那个π。

请原谅我比较懒,写这个程序的屏和我现在的屏有点不大一样,懒得设置了,贴张图给大家看看。

先把主函数贴出来,如果想要全部的代码,请去下载我的同名资源。

/**************************************************************************
***** 函数名:  main()
***** 功能:    电子钟
***** 参数:    无
***** 返回值:
***** 创建者:  潘星宇
***** 创建时间: 2012-12-26
***** 最后更新:2012-12-26
****************************************************************************/
int main(void)
{
 static U8 str[30] = {0};     //串口寄存数组
 U8 ch;                   //串口用变量
 static U8 UART_i = 0;       //串口数组计数用
 unsigned char hour_i;         //画小时刻度计数用的变量
 unsigned char minute_i;      //画分钟刻度计数用的变量
 U32 hour;       //从RTC读取的小时
 U32 minute;       //分钟
    U32 second;       //秒
 U32 second_ch;              //秒控制变量,用于控制每秒才显示一次
 static U32 hour_register;     //小时寄存器,用于储存上一次画时针的位置,用于清屏
 static U32 minute_register;     //分钟寄存器
 static U32 second_register;     //秒寄存器

 LCD_Init();                     //LCD初始化
 rGPBDAT = 0x5e0;             //关LED
 rGPBCON  =0x00115401;            //设置GPB5、GPB6、GPB8、GPB10为输出
 rGPBUP   =0x7ff;             //禁止上拉

 cal_cpu_bus_clk();       //获取时针频率
 Uart_Init( 0,115200 );      //初始化串口

 Brush_Background(0xc7e7);          //绘制背景
 RTC_time_set (0x11, 0x12, 0x26, 0x03, 0x14, 0x55, 0x35);

  draw_circle(180,136,123,0xd8a7);    //表外框
 /**********************************************
  *已知起始点、两点距离和角度画线的参数分别为:*
  *第一点坐标x1,y1;         *
  *长度;           *
  *角度(不是弧度);         *
  *颜色;                                      *
  *希望保留起始的点的个数;       *
  *希望保留最后的点的个数                      *
  **********************************************/
 //画小时刻度
 for (hour_i=0; hour_i<12; hour_i++)
 {
  one_point_to_another(180, 136, 120, hour_i*30, 0x5922, 0, 15);    //从0点画到11点
 }

 //画分钟刻度
 for (minute_i=0; minute_i<60; minute_i++)
 {
  if (minute_i % 5)           //整点处不画刻度
  {
   one_point_to_another(180, 136, 120, minute_i*6, 0x5922, 0, 1 ); //从1分画到59分
  }
 }

 while (1)
 {
  hour = rBCDHOUR;    //获取小时
  minute = rBCDMIN;   //获取分钟
  second = rBCDSEC;   //获取秒
  hour = ((hour / 16)*10 + hour % 16) % 12;//由于从RTC读出的都是16进制的,先转成10进制,再将24小时格式转化为12小时格式
  minute = (minute / 16)*10 + minute % 16;    
  second = (second / 16)*10 + second % 16;             
  if (second != second_ch)
  {
   //先清除掉上次画的时针,分针,秒针,长度暂分别定为35,60,90
   if ((hour_register*30 + (minute_register / 10)*5) != (hour*30 + (minute / 10)*5)) //为了消除分针和时针闪屏的问
   {                      //只有当分针或时针确实移动了位置才消除上一次画的的值
    one_point_to_another(180, 136, 120, hour_register*30 + (minute_register / 10)*5, 0xc7e7, 35, 0); 
   }
   if (minute_register != minute)
   {
    one_point_to_another(180, 136, 120, minute_register*6, 0xc7e7, 60, 0); 
   }
    one_point_to_another(180, 136, 120, second_register*6, 0xc7e7, 90, 0);

   second_ch = second;      //使秒控制变量等于当前时钟的秒值,这样直到秒变化,才再次进入这个内部来
   one_point_to_another(180, 136, 120, hour*30 + (minute /10)*5, 0xfb00, 35, 0);   //时针每小时走30°,分钟动10分钟,时钟动5°
   one_point_to_another(180, 136, 120, minute*6, 0x435c, 60, 0); 
   one_point_to_another(180, 136, 120, second*6, 0xf800, 90, 0);
   rtc_display_lcd(320,150,0xb1dd,0x08a8); //显示电子钟,参数分别为横坐标、纵坐标、颜色、背景色
   hour_register = hour;              //将时分秒的值储存起来,用于下一秒清除上一秒的变化
   minute_register = minute;
   second_register = second;
  }
  
  while (rUTRSTAT0 & 0x1)     //当串口接收缓冲器有数据时
  {
   ch = rURXH0;      //接收数据
   str[UART_i++] = ch;
   if((ch == 0xff)||(UART_i == 29 ))   //接收数据完成
   {
    RTC_time_set (str[0], str[1], str[2], str[3], str[4], str[5], str[6]);
    UART_i = 0;
      }
  } 
 }
}          

 这个还带串口修改RTC时间的功能

图如下

 

原创粉丝点击