历史上的今天
返回首页

历史上的今天

今天是:2025年12月07日(星期日)

正在发生

2022年12月07日 | 基于STM32单片机1602显示电子时钟

2022-12-07 来源:zhihu

1、本系统采用1602显示时分秒,4个按键可以调整时间,一个按键是选择按键,一个按键是加,一个按键是减,一个按键是退出按键。


2、当选择按键按下的时候,该选择位闪烁。

主函数代码:

#include "delay.h"

#include "sys.h"

#include "1602.h"

#include "timer.h"

#include "key.h"

u8 table1[]="liyang " ;

u8 table2[]="9876543210654321" ;

signed char miao,fen,shi;

u8 flag_100ms=0;

u16 count_ms=0;

u8 S1num;

/*******************主函数************************/

int main(void)

{

//u8 t;

delay_init();//延时函数初始化

LCD1602_Init();//LCD1602初始化函数

TIM3_Int_Init(999,71);//1MS

TIM4_Int_Init(999,71);//1MS

KEY_Init(); //按键初始化

#IF0 //注释掉程序

LCD_Write_Command(0x80);//第一行的首地址

for(i=0;i<16;i++)

{

LCD_Write_Date(table1);

delay_ms(3);

}

LCD_Write_Command(0xc0);//第二行的首地址

for(i=0;i<16;i++)

{

LCD_Write_Date(table2);

delay_ms(3);

}

#endif

while(1)

{


display(shi,fen,miao);

keyscan();

}



}

定时器代码

void TIM3_Int_Init(u16 arr,u16 psc)

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能


//定时器TIM3初始化

TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值

TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位


TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断

//中断优先级NVIC设置

NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能

NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器

TIM_Cmd(TIM3, ENABLE); //使能TIMx

}

//3中断服务程序

void TIM3_IRQHandler(void) //TIM3中断

{

static u16 count=0;

if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否

{

TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx更新中断标志

count++;

count_ms++;

if(count_ms==500)

{

count_ms=0;

flag_100ms=~flag_100ms;

}

if(count==1000)

{

count=0;

miao++;

if(miao==60)

{

miao=0;

fen++;

if(fen==60)

{

fen=0;

shi++;

if(shi==24)

shi=0;

}

}

}


}

}

//通用定时器4中断初始化

//这里时钟选择为APB1的2倍,而APB1为36M

//arr:自动重装值。

//psc:时钟预分频数

//这里使用的是定时器3!

void TIM4_Int_Init(u16 arr,u16 psc)

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能


//定时器TIM4初始化

TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值

TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式

TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位


TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM4中断,允许更新中断

//中断优先级NVIC设置

NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4中断

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4; //从优先级3级

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能

NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器

TIM_Cmd(TIM4, ENABLE); //使能TIMx

}

//定时器4中断服务程序

void TIM4_IRQHandler(void) //TIM3中断

{

if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否

{

TIM_ClearITPendingBit(TIM4, TIM_IT_Update ); //清除TIMx更新中断标志

count_ms++;

if(count_ms==500)

{

count_ms=0;

flag_100ms=~flag_100ms;

}

}

}


1602代码

#include"1602.h"

#include"sys.h"

#include "delay.h"

extern u8 S1num;

extern u8 flag_100ms;

/*******************LCD1602初始化************************/

void LCD1602_Init(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOA, ENABLE); //使能PA,PC端口时钟

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10; //LCD1602的三根控制线rs,rw,en

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出

GPIO_Init(GPIOE, &GPIO_InitStructure);//根据设定参数初始化GPIOE


GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;//LCD1602的8跟数据线 //LED1-->PE.5 端口配置, 推挽输出

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//IO口速度为50MHz

GPIO_Init(GPIOA, &GPIO_InitStructure);//根据设定参数初始化GPIOE

LCD_Write_Command(0x01);//清除屏幕显示

delay_ms(2);


LCD_Write_Command(0x38);//设置LCD两行显示,一个数据由5*7点阵表示,数据由8跟线传输

delay_ms(2);


LCD_Write_Command(0x06);//设定输入方式,增量不移位

delay_ms(2);

LCD_Write_Command(0x0c);//开整体显示,关光标,不闪烁

delay_ms(2);

}

/*******************写指令函数************************/

void LCD_Write_Command(u8 com)

{

GPIO_Write(GPIOA, 0X00FF&com);//该函数一般用来往一次性一个GPIO的多个端口设置

rs=0; //写指令函数和写数据函数其实可以并一起价格 BOOL变量判断是写指令就rs=0写数据就rs=1。

rw=0;

en=0; //写指令 输入:RS=L,RW=L,E=下降沿脉冲

delay_ms(2);

en=1;

delay_ms(2);

en=0;

}

/*******************写数据函数************************/

void LCD_Write_Date(u8 date)

{

GPIO_Write(GPIOA,0X00FF&date);//GPIOA的第八位用来作为8位数据口

rs=1;

rw=0;

en=0; //写指令 输入:RS=L,RW=L,E=下降沿脉冲

delay_ms(2);

en=1;

delay_ms(2);

en=0;

}

/******************************************************************************

函数名称:LCD1602_MoveToPosition

函数功能:将液晶的光标移动到指定的位置

入口参数:x-液晶显示的行数,范围0-1

x = 0:在液晶的第一行

x = 1:在液晶的第二行

y-液晶显示的列数,范围0-15

y = 0:在液晶的第一列

y = 1:在液晶的第二列

......

y = 15:在液晶的第十六列

返回值:无

备注:通过指定x,y的值可以将液晶的光标移动到指定的位置

*******************************************************************************/

void LCD1602_MoveToPosition(u8 x,u8 y)

{

if(0 == x)

LCD_Write_Command(0x80 | y); //光标定位到第一行的y列

if(1 == x)

LCD_Write_Command(0xC0 | y); //光标定义到第二行的y列

}

/******************************************************************************

函数名称:LCD1602_DisplayOneCharOnAddr

函数功能:在指定的位置上显示指定的字符

入口参数:x-液晶显示的行数,范围0-1

x = 0:在液晶的第一行

x = 1:在液晶的第二行

y-液晶显示的列数,范围0-15

y = 0:在液晶的第一列

y = 1:在液晶的第二列

......

y = 15:在液晶的第十六列

ucData-要显示的字符数据

返回值:无

备注:确保x,y的取值要在指定的范围内

*******************************************************************************/

void LCD1602_DisplayOneCharOnAddr(u8 x,u8 y,u8 ucData)

{

LCD1602_MoveToPosition(x,y); //光标位置

LCD_Write_Date(ucData); //写入数据

}

/******************************************************************************

函数名称:LCD1602_DisplayString

函数功能:显示字符串

入口参数:ucStr-字符串的首地址

返回值:无

备注:无

*******************************************************************************/

void LCD1602_DisplayString(u8 *ucStr)

{

while(*ucStr != '') //字符串结束之前,循环显示

{

LCD_Write_Date(*ucStr); //依次写入每一个字符

ucStr++; //指针增加

}

}

/*****************控制光标函数********************/

void write_guanbiao(u8 hang,u8 add,u8 date)

{

if(hang==1)

LCD_Write_Command(0x80+add);

else

LCD_Write_Command(0x80+0x40+add);

if(date == 1)

LCD_Write_Command(0x0f); //显示光标并且闪烁

else

LCD_Write_Command(0x0c); //关闭光标

}

void display(u8 aa,u8 bb,u8 cc)

{

if(S1num!=3)

{

LCD1602_DisplayOneCharOnAddr(1,0,aa/10+0x30);

LCD1602_DisplayOneCharOnAddr(1,1,aa%10+0x30);

}

else

{

if(flag_100ms==0)

{

LCD1602_DisplayOneCharOnAddr(1,0,' ');

LCD1602_DisplayOneCharOnAddr(1,1,' ');



}

else

{

LCD1602_DisplayOneCharOnAddr(1,0,aa/10+0x30);

LCD1602_DisplayOneCharOnAddr(1,1,aa%10+0x30);

}

}

LCD1602_DisplayOneCharOnAddr(1,2,':');

if(S1num!=2)

{

LCD1602_DisplayOneCharOnAddr(1,3,bb/10+0x30);

LCD1602_DisplayOneCharOnAddr(1,4,bb%10+0x30);

}

else

{

if(flag_100ms==0)

{

LCD1602_DisplayOneCharOnAddr(1,3,' ');

LCD1602_DisplayOneCharOnAddr(1,4,' ');



}

else

{

LCD1602_DisplayOneCharOnAddr(1,3,bb/10+0x30);

LCD1602_DisplayOneCharOnAddr(1,4,bb%10+0x30);

}

}

LCD1602_DisplayOneCharOnAddr(1,5,':');

if(S1num!=1)

{

LCD1602_DisplayOneCharOnAddr(1,6,cc/10+0x30);

LCD1602_DisplayOneCharOnAddr(1,7,cc%10+0x30);

}

else

{

if(flag_100ms==0)

{

LCD1602_DisplayOneCharOnAddr(1,6,' ');

LCD1602_DisplayOneCharOnAddr(1,7,' ');

}

else

{

LCD1602_DisplayOneCharOnAddr(1,6,cc/10+0x30);

LCD1602_DisplayOneCharOnAddr(1,7,cc%10+0x30);

}

}

}


推荐阅读

史海拾趣

捷嘉电子(Chequers Electronic)公司的发展小趣事

在国内市场取得一定成绩后,捷嘉电子开始将目光投向国际市场。通过与国外知名企业的合作,公司成功将产品出口到数十个国家。这一过程中,捷嘉电子不仅面临着文化差异、法律法规等挑战,还需要应对国际市场竞争的复杂性。然而,凭借着过硬的产品质量和专业的服务,捷嘉电子逐渐在国际市场上赢得了良好的口碑。

FORYARD公司的发展小趣事

在20世纪80年代初,FORYARD公司由一群电子工程师在硅谷创立。起初,公司专注于研发高性能的半导体芯片,但面临激烈的市场竞争和技术壁垒。为了突破重围,FORYARD决定聚焦于低功耗、高集成度的微控制器(MCU)领域。经过数年的潜心研发,公司成功推出了一款具有革命性创新的MCU产品,凭借其出色的性能和价格优势,迅速在智能家居和可穿戴设备市场占据一席之地。这一成功不仅为公司赢得了市场认可,也为后续的技术创新和业务拓展奠定了坚实基础。

Custom Electronics Inc公司的发展小趣事

随着科技的快速发展和市场需求的变化,CEI不断调整自身的发展战略和业务结构。公司加大了对新技术、新产品的研发投入,推动产业结构的升级和转型。同时,CEI还积极探索新的商业模式和市场机会,为公司的可持续发展提供了有力保障。

ATM [Advanced Technical Materials]公司的发展小趣事

随着科技的进步,ATM也在不断进行技术创新和升级。从最初的简单取款功能,到现在的存款、转账、查询等多种功能,ATM的功能日益丰富。同时,随着移动支付和互联网金融的兴起,ATM也在逐渐与这些新技术融合,提供更加便捷、安全的金融服务。

GREATECS公司的发展小趣事

20世纪60年代,英国人谢泼德·巴伦产生了发明一款“随时可以取钞票的机器”的想法,并付诸实践。1967年6月27日,世界上第一台ATM在英国伦敦北部的巴克莱银行亮相。这台机器的成功推出,标志着ATM开始进入公众的视野,并逐渐在全球范围内得到广泛应用。

冠西电子(COSMO)公司的发展小趣事

进入21世纪,冠西电子不断加大在研发方面的投入,成功突破了一系列技术难题,特别是在光耦合器和继电器领域取得了显著成果。这些技术突破不仅提升了冠西电子产品的竞争力,也为其赢得了更多国际大厂的信赖。同时,冠西电子积极拓展市场,其产品广泛应用于电源、互联网设备、电脑系统和相关系统、家电、工业、安防系统等领域,为全球客户提供优质的产品和服务。

问答坊 | AI 解惑

简易数字电压表的设计

采用ADC0809对模拟电压量进行采样,通过单片机AT89S51处理后由三位数码管进行显示。…

查看全部问答>

准备去海同的培训,请问参加过的兄弟到底怎么样

我现在本身有一份工作,但从事的不是自己喜欢的行业,考虑了很多,准备从事嵌入式行业。 但是没有相关工作经验,投了很多简历没有公司要的,所以想通过参加培训的方式过渡到这个行业。 在51JOB上看到海同培训有这个机会,刚才也亲自去现场查看过 ...…

查看全部问答>

为什么使用32.768kHz

晶体振荡器的频率为什么选择32.768kHz? 有什么优点?是如何考虑的?…

查看全部问答>

非自动变量(Non-automatic variables) 有人给解释一下这个东西吗

一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 有人给解释一下这个东西吗 来自电子工程师技术交流(12425841) …

查看全部问答>

求救STM8UART多机

本人已学生,参加比赛,现在要用UART多机 长话短说,我用的是地址唤醒,思路是这样的,主机需要信息时发送地址给从机(红外遥控发的),从机把相应的信息(温度、湿度)回应给主机,但过程中出现了几个问题: 1  1主1从用地址识别 ...…

查看全部问答>

2011电赛--专科--三道题--简析

本帖最后由 paulhyde 于 2014-9-15 03:55 编辑 2011电赛专科三道题相对于往届是比较简单的,但是其每一题都有关键技术点   波形存储与回放的技术关键点在于一个特殊的要求-----功耗小于50mW,基本就属于指定了MCU类型,TI的430;要求内含AD ...…

查看全部问答>

专用型高精度24位AD转换器芯

专用型高精度24位AD转换器芯…

查看全部问答>

开发板USB转串口驱动

很多同学都说连接电脑连接不上,很多都是USB转串口驱动安装的问题,要是win7的系统的话请下载(WIN7 系统USB转串口驱动),如果是XP的请下载(XP系统USB转串口驱动)。…

查看全部问答>

南华大学黄智伟 备战2013电子设计竞赛 选择ADC和DAC电压基准的一些考虑

本帖最后由 paulhyde 于 2014-9-15 03:44 编辑 南华大学黄智伟 备战2013电子设计竞赛 选择ADC和DAC电压基准的一些考虑 因为数据转换系统的精度非常依赖于由内部或者外部直流(DC)电压基准所确定电压的精度,所以在设计一个数据转换系统时,选 ...…

查看全部问答>

MSP430程序升级方式探讨

对MSP430系列单片机进行编程的方式有以下3种:利用JTAG接口,利用BSL固件和利用用户自定义的升级固件。由于利用自定义升级固件进行程序升级的方式比较灵活,且用途广泛,因此本文将对其作重点介绍。 1 利用JTAG接口 MSP430系列的单片机都集成了J ...…

查看全部问答>