发新帖本帖赏金 10.00元(功能说明)我要提问
12下一页
返回列表
打印
[STM32]

求助各位坛友:定时器2ms数码管动态显示 3位数只显示最...

[复制链接]
4400|47
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
qmmdzd|  楼主 | 2018-4-5 18:48 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
各位坛友,我把数码管显示函数放在while(1)里面的话能显示3位,放在定时器里就只显示一个数了,共阳极数码管位选和段选都用STM32的IO驱动,定时器定时2ms,程序如图片所示,求助各位如何能在定时器下数码管动态显示三位呢?

1.png (243.84 KB )

1.png

2.png (18.2 KB )

2.png

3.png (52.91 KB )

STM32的IO口直接驱动共阳极数码管

STM32的IO口直接驱动共阳极数码管

4.png (48.35 KB )

4.png
评论
xch 2018-4-8 23:10 回复TA
@qmmdzd :你这函数已经成功,调用一次就显示3位。 改成调用一次就显示其中一位。调用三次完成1轮显示。 
qmmdzd 2018-4-8 22:34 回复TA
@xch :按照我这个写法延时去了有问题 我试过 去了显示就乱了 不过你说的变量改变方式用static我还没试 就把k设置为全局变量了 中断加一 依旧不行 
xch 2018-4-8 17:13 回复TA
@qmmdzd :按照k值提取对应十进制数字进行显示,去掉延时函数 
xch 2018-4-8 17:10 回复TA
@qmmdzd :很简单啊!你的程序你已经证明基本可用。 问题在DisPlayNum()之中的k 变量。 把它改成static int k=0; 每次调用+1,去掉for 循环, 0 1 2 0 1 2........  
qmmdzd 2018-4-8 09:26 回复TA
@xch :如果一次显示一位 程序该如何写呢? 
xch 2018-4-7 22:09 回复TA
@qmmdzd :一次中断显示一位数码管,定时中断显示还可以确保显示亮度均匀 
qmmdzd 2018-4-7 20:50 回复TA
@xch :坛友意思是其它两个扫描太快看不出来?那该如何解决呢 
xch 2018-4-6 21:28 回复TA
没看到中断如何显示。 是不是一次中断把3个LED都驱动了? 结果最后一个被驱动的点亮,其他应该很昏暗没看出来 

相关帖子

沙发
qmmdzd|  楼主 | 2018-4-5 19:02 | 只看该作者
用单片机和三极管直接驱动的数码管,没用驱动芯片,Display显示函数按个、十、百位扫描,i作为定时器标志位,赋值为1

使用特权

评论回复
板凳
jimsboy| | 2018-4-5 23:54 | 只看该作者
ST的好办,连上STlink,下断点,检查

使用特权

评论回复
地板
xyz549040622| | 2018-4-6 07:05 | 只看该作者
要保证在人的视觉残留范围内(20ms)保证每位数码管都进行点亮一次,那么保证7ms左右,进行一个数码管的点亮,依次点亮三个就好了。没看懂你的显示函数,要把显示函数放到定时器中,保证7ms扫描一次,依次扫描每个数码管就好了。

使用特权

评论回复
5
qmmdzd|  楼主 | 2018-4-6 09:21 | 只看该作者
xyz549040622 发表于 2018-4-6 07:05
要保证在人的视觉残留范围内(20ms)保证每位数码管都进行点亮一次,那么保证7ms左右,进行一个数码管的点亮 ...

我这里的显示函数是写入3位数,K=0时候写入个位,K=1时写入十位,K=2写入百位,case0-case3扫描位选,write_table【】写入段选数据,这样用定时器不停的扫描,只亮一个  i是放在定时器的全局变量,由1变0时候开始显示函数写入,就是if(!i),请问这样写有什么问题呢?

使用特权

评论回复
6
qmmdzd|  楼主 | 2018-4-6 09:25 | 只看该作者
jimsboy 发表于 2018-4-5 23:54
ST的好办,连上STlink,下断点,检查

检查过了,显示部分display函数执行时间很短,几十个微妙,这样看来定时2毫秒足够扫描三个数码管,而不应该只显示一个数码管啊?

使用特权

评论回复
7
qmmdzd|  楼主 | 2018-4-6 09:27 | 只看该作者
定时器里面是每次定时中断扫描一个管子呢?还是一次中断扫描三只数码管呢?

使用特权

评论回复
8
xyz549040622| | 2018-4-6 13:26 | 只看该作者
qmmdzd 发表于 2018-4-6 09:25
检查过了,显示部分display函数执行时间很短,几十个微妙,这样看来定时2毫秒足够扫描三个数码管,而不应 ...

几十个us够了吗?你有个delayms的函数,起码执行一次,最少3ms,程序的可读性太差了,i居然当做全局变量,你这样写,只要i为0的时候,才执行扫描一遍显示函数,都超过定时器时间了,太乱了。

使用特权

评论回复
9
qmmdzd|  楼主 | 2018-4-6 14:49 | 只看该作者
xyz549040622 发表于 2018-4-6 13:26
几十个us够了吗?你有个delayms的函数,起码执行一次,最少3ms,程序的可读性太差了,i居然当做全局变量 ...

不好意思,那行delay()忘记删除掉了,在调试时候那行程序隐藏了,我也没学过编程,全是自学自编,水平太低,希望版主指教一下,给写个定时器显示三位数的子程序  感激不尽

使用特权

评论回复
10
jimsboy| | 2018-4-6 17:43 | 只看该作者
qmmdzd 发表于 2018-4-6 09:25
检查过了,显示部分display函数执行时间很短,几十个微妙,这样看来定时2毫秒足够扫描三个数码管,而不应 ...

你肯定有一个函数是用来控制给三个公共端轮流加电的,你在这里下断点,看它第一次来的时候,是不是第一个数码管点亮,第二次是不是第二个点亮,因为你下了断点,程序跑到那里停住了,你可以清楚地看到现在是哪个管子被点亮了。如果它的运行结果和你预期的不一致,再分析为什么这里没按你思路来。就可以解决了。

使用特权

评论回复
11
xyz549040622| | 2018-4-6 21:32 | 只看该作者
qmmdzd 发表于 2018-4-6 14:49
不好意思,那行delay()忘记删除掉了,在调试时候那行程序隐藏了,我也没学过编程,全是自学自编,水平 ...

你把代码贴上来,不要截图,我给你修改备注看看。

使用特权

评论回复
12
qmmdzd|  楼主 | 2018-4-6 22:00 | 只看该作者
xyz549040622 发表于 2018-4-6 21:32
你把代码贴上来,不要截图,我给你修改备注看看。


void Isr_Init()
{
        NVIC_InitTypeDef NVIC_InitStructure;
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
        NVIC_InitStructure.NVIC_IRQChannel =TIM3_IRQn;// TIM3_IRQChannel;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init (&NVIC_InitStructure);                
}

void TIM3_IRQHandler(int x ) //10ms
{                                          
    u8 st;
        st=TIM_GetFlagStatus(TIM3, TIM_IT_Update);
  if(st!=0)
        {  
          DisPlayNum(x);       //x为随时写入数码管的数据  
                TIM_ClearFlag(TIM3, TIM_IT_Update);
        }
}

int main(void)
{       
       SMG_GPIO_init();
         Timer3_init();
         Isr_Init();
         TIM3_IRQHandler(000);

        while(1)
        {
                ;;//主程序执行其他任务
        }
}
以下是显示部分函数
u16 table[]={0x0180,0x9f80,0x2680,0x0e80,0x9c80,0x4c80,0x4480,0x1f80,0x0080,0x0880};

void SMG_GPIO_init(void)  
{  
         GPIO_InitTypeDef GPIO_InitStructure;  
         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
       

         GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15|GPIO_Pin_14|GPIO_Pin_13|GPIO_Pin_12|GPIO_Pin_11|GPIO_Pin_9|GPIO_Pin_8|GPIO_Pin_7;  
         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;  
         GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;  
         GPIO_Init(GPIOE,&GPIO_InitStructure);
         GPIO_SetBits(GPIOE,GPIO_Pin_15|GPIO_Pin_14|GPIO_Pin_13|GPIO_Pin_12|GPIO_Pin_11|GPIO_Pin_9|GPIO_Pin_8|GPIO_Pin_7);
       
        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13|GPIO_Pin_12|GPIO_Pin_11;  
        GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;  
        GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;  
        GPIO_Init(GPIOF,&GPIO_InitStructure);
        GPIO_SetBits(GPIOF,GPIO_Pin_13|GPIO_Pin_12|GPIO_Pin_11);
}

void DisPlayNum(int n) //nΪÈýλÊý
{  
    if (n < 1000)  
               
    {
           int k;

       for (k=0;k<3;k++)  
        {  
                                          
         GPIO_SetBits(GPIOF,GPIO_Pin_13|GPIO_Pin_12|GPIO_Pin_11);//关闭所有数码管位选
         GPIO_SetBits(GPIOE,GPIO_Pin_15|GPIO_Pin_14|GPIO_Pin_13|GPIO_Pin_12|GPIO_Pin_11|GPIO_Pin_9|GPIO_Pin_8|GPIO_Pin_7);//关闭所有段选
            switch (k)  //0-2循环,选中位选
            {  
                case 0:  
                    GPIO_ResetBits(GPIOF,GPIO_Pin_13);   
                    break;  
                case 1:  
                    GPIO_ResetBits(GPIOF,GPIO_Pin_12);  
                    break;  
                case 2:  
                    GPIO_ResetBits(GPIOF,GPIO_Pin_11);
                    break;                 
            }  

           switch (n % 10)  //对应位选 依次取个十百位
            {  
                case 0:  
                    GPIO_Write(GPIOE,table[0]);   
                    break;  
                case 1:                                                      
                    GPIO_Write(GPIOE,table[1]);
                    break;  
                case 2:  
                    GPIO_Write(GPIOE,table[2]);  
                    break;  
                case 3:  
                    GPIO_Write(GPIOE,table[3]);  
                    break;  
                case 4:  
                    GPIO_Write(GPIOE,table[4]);
                    break;  
                case 5:  
                    GPIO_Write(GPIOE,table[5]);  
                    break;  
                case 6:  
                    GPIO_Write(GPIOE,table[6]);  
                    break;  
                case 7:  
                    GPIO_Write(GPIOE,table[7]);  
                    break;  
                case 8:  
                    GPIO_Write(GPIOE,table[8]);  
                    break;  
                case 9:  
                    GPIO_Write(GPIOE,table[9]);  
                    break;  
            }     
          n = n / 10;  
          if (n==0)  
           break;
       }  
                             
    }
       
        }               
               


使用特权

评论回复
13
xyz549040622| | 2018-4-7 08:13 | 只看该作者
1.    首先你的定时器时间,人的视觉残留是20ms,想要用到定时器扫描数码管,那么就有两种选择:
1>  每隔20ms,定时器扫描三位数码管一次,这样的缺点是数码管消耗的电流会有点大,因为同时点亮三只数码管。要保证数码管的扫描时间小于20ms,即中断时间。
2>  每隔6.5ms,扫描一位数码管,确保在20ms内要扫描完毕三位数码管,这样的缺点是程序的设计稍微要复杂点,但是功耗会降低。
2.    你程序中的设计其实执行的是上面的第一种做法,同一时间扫描三位数码管。
3.    你STM32程序的写法,TIM3_IRQHandler(int x )中断函数居然带了参数,建议你先调通定时器的用法,快速的办法是移植别的定时器程序,在定时器中断中进行led的闪烁,先把这个调通然后再把显示函数加进去就好了。
4.    再说显示函数,你暂时不用定时器,主函数while中执行显示函数,显示正确的话,放到定时器中就好了。显示函数看着没什么大的毛病,应该是可以运行的。

使用特权

评论回复
14
qmmdzd|  楼主 | 2018-4-7 10:04 | 只看该作者
xyz549040622 发表于 2018-4-7 08:13
1.    首先你的定时器时间,人的视觉残留是20ms,想要用到定时器扫描数码管,那么就有两种选择:1>  每隔20 ...

感谢坛主回复 中断函数改为下面的了,
void TIM3_IRQHandler( void) //20ms一次
{                                          
          if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
  {
                DisPlayNum(n);
                TIM_ClearFlag(TIM3, TIM_IT_Update);
        }
}
void Timer3_init()
{         
                TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
                RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM3,ENABLE);
                TIM_TimeBaseStructure.TIM_Period =20000-1; //定时20ms
                TIM_TimeBaseStructure.TIM_Prescaler =72-1;              
                TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;                                
                TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //ÏòÉϼÆÊý
                 TIM_DeInit(TIM3);
                TIM_TimeBaseInit(TIM3, & TIM_TimeBaseStructure);
                TIM_Cmd(TIM3, ENABLE);          
                TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //ÏòÉϼÆÊýÒç³ö²úÉúÖжÏ

}
    但是DisPlayNum(n);这条如果不放在while(1),数码管总是亮一个百位,也就是函数中的case2;
定时时间是20ms,修改这时间也没用,疑惑的地方在于为什么这种写法没有点亮三个管子,只亮最后一个?
如果想亮三个,该如何修改呢

使用特权

评论回复
15
jinglixixi| | 2018-4-7 11:11 | 只看该作者
把用于显示位置的计数器设为全局变量,否则每次只能从1开始,也就只显示1位了!

使用特权

评论回复
16
qmmdzd|  楼主 | 2018-4-7 13:04 | 只看该作者
jinglixixi 发表于 2018-4-7 11:11
把用于显示位置的计数器设为全局变量,否则每次只能从1开始,也就只显示1位了! ...

也就是把K设置为全局变量?Display中的K的for循环也用改吗?

使用特权

评论回复
17
xyz549040622| | 2018-4-7 18:04 | 只看该作者
qmmdzd 发表于 2018-4-7 10:04
感谢坛主回复 中断函数改为下面的了,
void TIM3_IRQHandler( void) //20ms一次
{                                          

1.你的20ms是否准确呢?
2.跟踪看定时器中的k三遍是否都执行完毕
3.关闭所有的段选和位选放到for循环外面试试。

使用特权

评论回复
18
qmmdzd|  楼主 | 2018-4-7 19:38 | 只看该作者
xyz549040622 发表于 2018-4-7 18:04
1.你的20ms是否准确呢?
2.跟踪看定时器中的k三遍是否都执行完毕
3.关闭所有的段选和位选放到for循环外面 ...

软件仿真了下 就一个端口有电平变化 每次都是第三个 是不是这样写显示函数有问题?麻烦坛主给写一个显示函数  我试一下
你说的把段选和位选关闭放在for外边 三个数码管都微亮了  实在找不出问题在哪

1.png (297.07 KB )

1.png

2.png (39.77 KB )

2.png

使用特权

评论回复
19
10位3年以上的单片机开发人员,只有1人写过数码管驱动

使用特权

评论回复
20
qmmdzd|  楼主 | 2018-4-7 20:52 | 只看该作者
山东电子小菜鸟 发表于 2018-4-7 20:03
10位3年以上的单片机开发人员,只有1人写过数码管驱动

不用驱动芯片是不是会麻烦很多呢?求显示程序上的指点

使用特权

评论回复
发新帖 本帖赏金 10.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

8

主题

93

帖子

0

粉丝