基于STC32G12K128实验箱V9.62_实验之数码管显示字符串
许多单片机学习开发板,都带有数码管显示模块,STC32G12K128实验箱V9.62版实验箱,也不例外, 带有8位数码管。
可见单片机驱动数码管显示, 是一项基本功技术, 必须学习掌握的。
八段数码管笔划少,可显示的字符有局限,但显示数值型数据应该是足以胜任的,因此一些电子产品常用之。
看似简单,但对于新手来说,数值转段码,加之要动态扫描,稳定显示,编程上其实有一定难度,是不能小觑的 。
......
那么, 如果要显示一个数值,例如圆周率 3.14,或者显示多小数位 3.14159,该如何显示呢?
当然,对应数码管的位数,首先要有一个显示缓存区,将需要显示的数字一个一个转换成段码往里填就是了......
经常能看到例程中有类似这样的代码:
temp1=temp;
qian=temp1/1000;
bai=temp1%1000/100;
shi=temp1%100/10;
ge=temp1%10;
为了显示一个四位数, 要四次除以、取整、求余..... 当然, 这个算法是绝对没错的, 也可谓是经典的....
但如果要显示一个6位数呢? 或者显示数值小数点后位数是不同的呢? 那么显示算法都要分别重写了, 而且代码的行数显然也是不同的了....
是否能写一个函数, 以不变应万变呢? 于是就尝试做了这样的实验: 数码管显示字符串函数...
先看看效果:
视频中,
先显示一个字符串 "Stc-Abcd",延时3秒...(体现直接显示字符串)
再显示: "P=3.14159" ,延时3秒... (亮相PI值,体现字母符号+小数显示)
再显示: "d= 1.234 " ,延时3秒... (直径数据,体现小数点后不同位数的显示)
再显示: "F=1.19597" , 延时3秒... (面积数据)
再循环显示: 时-分-秒 (体现整型数值的显示)
本帖最后由 浦江一水 于 2024-4-14 17:32 编辑
主程序 Main.C
( 主频 24MHz )
//********************************************************************************
// 名称: 实验8位数码管动态扫描显示
// 基于: STC32G12K128 V9.62 实验箱
// 实验: STC32G工程创建/STC-USB-Link1D仿真器调试/GPIO基本输出控制/定时器x中断...
// 编程: 浦晓明(浦江一水) 2023-12-10
//********************************************************************************
#include "STC32G_SYS.H"
#include "LED8D.H"
#definePI 3.14159
/************* 全局变量声明 **************/
char S; //显示字符串缓存
unsigned char hour; //时
unsigned char minute;//分
unsigned char second;//秒
float d,F; //直径和面积
/*************外部函数声明和外部变量声明 *****************/
//========================================================================
// 函数: voiddelay_ms(unsigned int ms)
// 描述: 毫秒级延时函数。
// 参数: ms,要延时的ms数,自动适应主时钟.
//=====================================================================
voiddelay_ms(unsigned int ms)
{ unsigned int i;
do{ i = MAIN_Fosc / 6000;
while(--i);
} while(--ms);
}
/********************** RTC演示函数 ***********************/
void RTC(void)
{
if(++second >= 60)
{ second = 0;
if(++minute >= 60)
{ minute = 0;
if(++hour >= 24) hour = 0;
}
}
sprintf(S,"%02d-%02d-%02d",hour,minute,second);//组织字符串
LED8D_Str(0,S); //送显示段码缓存
}
/******************** 主函数 **************************/
void main(void)
{
SYS_Init(); //系统初始化
LED8D_Init(); //8数码管初始化
d=1.234; //直径m
F=PI*d*d/4; //求面积
LED8D_Str(0,"Stc-ABcD"); //字符串常量直接显示
delay_ms(3000); //延时一下
sprintf(S,"P=%6.5f",PI); //浮点数转字符串显示PI值
LED8D_Str(0,S); //送显示段码缓存
delay_ms(3000); //延时一下
sprintf(S,"D= %5.3f ",d); //浮点数转字符串显示d直径值
LED8D_Str(0,S); //送显示段码缓存
delay_ms(3000); //延时一下
sprintf(S,"F=%6.5f",F); //浮点数转字符串显示F面积值
LED8D_Str(0,S); //送显示段码缓存
delay_ms(3000); //延时一下
hour = 11; //时间初始化
minute = 59;
second = 58;
RTC();
//循环...整型数转字符串显示...
while(1)
{
if(SecOK) //1s到
{ SecOK = 0;
RTC();
}
}
}
//完整工程源码文件见1楼
本帖最后由 浦江一水 于 2024-4-14 17:47 编辑
显示函数详解
//********************************************************************************
// 名称: LED8D.H LED_8位数码管驱动头文件
// 基于: STC32G12K128 V9.62 实验箱
// 实验: C251编译环境搭建/STC32G工程创建/STC-USB-Link1D仿真器调试/GPIO基本输出控制...
// 数码管动态扫描显示...
// 硬件: P6--8段数据 P7--8位驱动
// 编程: 浦晓明(浦江一水) 2023-12-13
//********************************************************************************
#ifndef__LED8D_H
#define__LED8D_H
#include "STC32G_SYS.H" //参考STC库函数, 局部修改...自备用.
#include "STC32G_GPIO.H" //参考STC库函数, 局部修改...自备用.
#include "STC32G_TIMER.H" //参考STC库函数, 局部修改...自备用.
extern bit SecOK; //秒到标志
//***** 端口功能初始化函数说明 *********************************************
void LED8D_Init(void); //初始化
void LED8D_Str(u8 x,u8 * str); //定位x显示字符串
#endif
//********************************************************************************
// 名称: LED8D.C LED_8位数码管
// 基于: STC32G12K128 V9.62 实验箱
// 实验: C251编译环境搭建/STC32G工程创建/STC-USB-Link1D仿真器调试/GPIO基本输出控制...
// 数码管动态扫描显示...
// 硬件: P6--8段数据 P7--8位驱动
// 编程: 浦晓明(浦江一水) 2023-12-10
//********************************************************************************
#include "LED8D.H"
/************* 用户定义宏 ***********************/
#define T0_Reload (65536UL -(MAIN_Fosc / 1000)) //Timer 0 中断频率, 1000次/秒
/************* 本地常量声明 *******************/
u8 code LedC[]={ "0123456789ABCDEF -HJKLNoPUtGqryhnuc=_S" }; //可显示字符38
u8 code LedD[]={ 0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,//0..9,A..F 段码...
0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x6E,0x7C, //...h 段码...
0x54,0x1C,0x58,0x48,0x08,0x6D, }; //n..._ 段码...
u8 code T_COM[]={ 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01,}; //位码
/************* 本地变量声明 **************/
u8LED8; //显示段码缓冲
u8DispN; //显示位索引
bit SecOK; //秒到标志
unsigned int msecond; //毫秒
/************* 本地函数声明 **************/
//***** LED8位数码管初始化函数 ******************************
void LED8D_Init(void)
{
SetGPIO_MODE(6,0xFF,3); //设P6口全部为开漏输出模式 //等效写法: SetGPIO_MODE(GPIO_P6,Pin_All,GPIO_OUT_OD);
SetGPIO_MODE(7,0xFF,0); //设P7口全部为准双向口模式 //等效写法: SetGPIO_MODE(GPIO_P7,Pin_All,GPIO_PullUp);
//P6=0x80;P7=0x7F;
TimerInit(0,0,1,(u16)T0_Reload); //定时器0模式:16位自动重载模式,12T...
TimerNVIC(0,0,1); //定时器0中断(优先级0)使能
DispN=0; msecond=0; EA=1; //开中断..
}
//*** 定位x显示字符串 ****************************************
void LED8D_Str(u8 x,u8 * str)
{ unsigned char ch,y,i;
if(x>=8)return; //超界..返回..
for(;x<8;x++) //从指定位置x到7
{ y=0;
for(i=0;i<38;i++) //根据字符查找对应段码
{ if(*str==LedC) //找到匹配
{ ch=LedD; //取出段码
y=1; break; //找到标志y
} else ch=0x00; //若无对应段码则用0空替代.
}
if(y==1) //有可显示字符...
{ if(*(str+1)=='.') //如果遇小数点处理...
{ ch |= 0x80; //段码最高位置1...(加小数点)
str++; //字符串指针移动
}
LED8=ch; //定位写入显示
}
str++; //字符串指针移动
if(*str=='\0')break; //字符串结束
}
}
//************************* 显示扫描函数 *******************
void DisplayScan(void) //由定时器0每1ms调用一次 8位数码管8ms动态扫描显示一遍.
{
P7 = ~T_COM; //移位驱动
P6 = ~LED8; //段码送出
if(++DispN >= 8)DispN = 0;//8位结束回0
}
//========================================================================
// 函数: Timer0_ISR_Handler
// 描述: Timer0中断函数.
//========================================================================
void Timer0_ISR_Handler (void) interrupt TMR0_VECTOR //进中断时已经清除标志
{
msecond++; //毫秒计数...
if(msecond>=1000){ SecOK=1; msecond=0; }
DisplayScan(); //1ms扫描显示一位
}
//**** RunLED END *********************************************
确实,,直接统一按照字符串来解析 要比获取一位一位的数字要简单很多,
,,如果资源比较充裕还可以使用类似sprintf函数来生成字符串,十分好用,所见即所得,
如果要移植其他的位数的芯片 只用做好字库适配,,不同芯片实际的写入函数的方法,就可以按照以前的方式来使用, 不错,学习了 _奶咖君_ 发表于 2024-4-14 17:36
确实,,直接统一按照字符串来解析 要比获取一位一位的数字要简单很多,
,,如果资源比较充裕还可以使用类似 ...
确实是金牌会员, 一眼就看出了使用sprintf()函数, 本实验就是, 在写具体函数应用时, 就简单多了.
谢谢您的浏览回复.
顺便聊一句, sprintf()函数在C251编译器环境下,有时会出现很奇怪的现象, 原因尚未搞清楚. 相比较在C51编译器环境下,要好一点.在Keil MDK for STM32 环境下, 从未出现过问题. 感觉 C251 比较小众,对STC32G并未全心支持... xxxevery 发表于 2024-4-14 18:36
不错,学习了
谢谢您的浏览回复鼓励.我们共同学习.... 浦江一水 发表于 2024-4-14 19:08
确实是金牌会员, 一眼就看出了使用sprintf()函数, 本实验就是, 在写具体函数应用时, 就简单多了.
谢谢您 ...
应该是长度对齐问题,可以使用(int)进行类型强制转换后再次尝试,并且保证输入参数类型和实际对应 学习了 王昱顺 发表于 2024-4-14 19:22
应该是长度对齐问题,可以使用(int)进行类型强制转换后再次尝试,并且保证输入参数类型和实际对应 ...
谢谢您的提醒指点...
根据坛子里坛友的一些疑问帖子来看:
C251 对于STC32G
不支持局部变量、形参变量的仿真跟踪...
不支持strstr()函数...
不支持全局变量的动态刷新...
与KeilMDK for STM32的使用顺畅感受有许多不同,相差甚远...
这些不爽, 在学习使用STC32G中, 十分挫伤学习STC32G的热情...
故而一直存有两点疑惑:
1. C251 很小众, 本无全力支持你STC之意, Keil C 本应有的功能,到此失灵...
2. STC的仿真驱动程序算法不够强壮...
页:
[1]