最近在做iic实验的过程中发现手边刚好有一个数码管他的驱动正好是使用iic,话不多说直接上代码了
这里就不赘述iic通信的原理和时序图了,网上相关资源也很多这里就附一篇iic的文章,这篇文章相当详细。文章链接
以下是数码管文件,其实这里我使用了太多的全局变量,这是一个不好的行为,代码还有极大的优化空间,同时这里要提醒自己extern的优缺点,优点就是可以节省一部分内存,缺点呢就是会降低程序运行效率,并且要注意extern在使用时,必须要在某一处有一个完整的定义。
第一部分:IIC初始化
OLED.C
这一部分主要是对于tm1637的各个引脚的初始化,并且声明了一系列的全局变量(可以为你后期做一些扩展使用)。
#include "oled.h"
#include "sys.h"
#include "delay.h"
void TM1637_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = TM1637_CLK_GPIO_PIN|TM1637_DIO_GPIO_PIN; //DIO CLK
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//²úÉúiicÆðʼÐźÅ
//START:when CLK is high,DATA change form high to low
void timestart(void)
{
TM1637_DIO_OUT();
TM1637_CLK=1;
TM1637_DIO=1;
delay_us(5);
TM1637_DIO=0;
delay_us(5);
TM1637_CLK=0;//ǯסiic×ÜÏß ×¼±¸·¢ËÍÊý¾Ý
}
//²úÉúiicÍ£Ö¹ÐźÅ
//STOP:when CLK is high DATA change form low to high
void timestop(void)
{
TM1637_DIO_OUT();
TM1637_CLK=1;
TM1637_DIO=0;
delay_us(4);
TM1637_DIO=1;
delay_us(4);
}
//ʱÖÓÔڸߵãдÈëÊý¾ÝÈ»ºó¾¹ýÒ»´ÎµÍµçƽת»»µ½Ï´ÎдÈëÊý¾Ý ÏÈ·¢ËÍ×îµÍλÊý¾Ý ÇÒÑÓʱ ҪСÓÚ4.7us
void iicwritebyte(unsigned char data)
{
unsigned char t;
TM1637_DIO_OUT();
TM1637_CLK=0;
for (t=0;t<8;t++)
{
TM1637_DIO=(data&0x01);
data>>=1;
delay_us(4);
TM1637_CLK=1;
delay_us(4);
TM1637_CLK=0;
delay_us(4);
}
}
u8 iicwaitack(void)
{
u8 ack=0;
TM1637_DIO_IN();
TM1637_CLK=1;
TM1637_DIO=1;
delay_us(4);
if(TM1637_DIO)
{
ack=1;
}
else{
ack=0;
}
TM1637_CLK=0;
delay_us(4);
return ack;
}
//²úÉúackÓ¦´ð
void iicack(void)
{
TM1637_DIO_OUT();
TM1637_CLK=0;
TM1637_DIO=0;
delay_us(2);
TM1637_CLK=1;
delay_us(2);
TM1637_CLK=0;
}
void iicnack(void)
{
TM1637_DIO_OUT();
TM1637_CLK=0;
TM1637_DIO=1;
delay_us(2);
TM1637_CLK=1;
delay_us(2);
TM1637_CLK=0;
}
u8 iicread(u8 ack)
{
u8 i,data=0;
TM1637_DIO_IN();
TM1637_CLK=0;
delay_us(5);
for(i=7;i>=0;i--)
{
TM1637_CLK=1;
delay_us(5);
data|=(TM1637_DIO)<<i;
TM1637_CLK=0;
delay_us(5);
}
if(ack)
{
iicack();
}else{
iicnack();
}
return data;
}
void timedisplay(void)
{
unsigned char i;
timestart();
iicwritebyte(0x40);
iicack();
timestop();
timestart();
iicwritebyte(0xC0);
iicack();
for(i=0;i<6;i++)
{
iicwritebyte(displaynum[i]);
iicack();
}
timestop();
}
void TM1637_SetBrightness( unsigned char level )
{
timestart();
iicwritebyte(0x88|level);
iicack();
timestop();
}
void xianshitime(int hour,int min)
{
int temp;
if(hour>9)
{
int temp2 = hour;
temp=temp2 % 10;
displaynum[1]=tab[temp];
temp2/=10;
displaynum[0]=tab[temp2];
}
if(hour<10)
{
displaynum[0]=tab[0];
displaynum[1]=tab[hour];
}
if(min>9)
{
int temp2 = min;
temp=temp2 % 10;
displaynum[3]=tab[temp];
temp2/=10;
displaynum[2]=tab[temp2];
}
if(min<10)
{
displaynum[2]=tab[0];
displaynum[3]=tab[min];
}
}
OLED.H
在头文件中声明相关的变量,同时将数据线利用寄存器配置io口的方法,快速配置数据线的输入输出。
#ifndef _OLED_H_
#define _OLED_H_
#include "stm32f10x.h"
#include "delay.h"
#define TM1637_CLK_GPIO_PIN GPIO_Pin_6
#define TM1637_DIO_GPIO_PIN GPIO_Pin_7
#define TM1637_CLK PBout(6)
#define TM1637_DIO PBout(7)
#define TM1637_READ_DIO PBin(7)
#define TM1637_DIO_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define TM1637_DIO_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
extern int min,hour,heart;
extern unsigned char tab[];
extern unsigned char displaynum[];//数码管显示数组
void TM1637_Init(void);
void TM1637_SetBrightness( unsigned char level );
void timestart(void);
void timestop(void);
void iicwritebyte(unsigned char data);
u8 iicwaitack(void);
void iicack(void);
void iicnack(void);
u8 iicread(u8 ack);
void xianshitime(int hour,int min);
// void fuhaoshanshuo(int count);
void timedisplay(void);
// void dongtaixianshi(int k);
#endif
第二部分:IIC通信
以下部分就是根据iic通信的逻辑时序图进行编写,首先就是iic的空闲状态,再者就是实现一个传输逻辑的完整过程,包括开始信号,停止信号,应答信号,无应答信号等。(这一部分和上面整体的文件是重复的为了上述文件的完整性所以又单另提取出来了)
OLED.C
//²úÉúiicÆðʼÐźÅ
//START:when CLK is high,DATA change form high to low
void timestart(void)
{
TM1637_DIO_OUT();
TM1637_CLK=1;
TM1637_DIO=1;
delay_us(5);
TM1637_DIO=0;
delay_us(5);
TM1637_CLK=0;//ǯסiic×ÜÏß ×¼±¸·¢ËÍÊý¾Ý
}
//²úÉúiicÍ£Ö¹ÐźÅ
//STOP:when CLK is high DATA change form low to high
void timestop(void)
{
TM1637_DIO_OUT();
TM1637_CLK=1;
TM1637_DIO=0;
delay_us(4);
TM1637_DIO=1;
delay_us(4);
}
//ʱÖÓÔڸߵãдÈëÊý¾ÝÈ»ºó¾¹ýÒ»´ÎµÍµçƽת»»µ½Ï´ÎдÈëÊý¾Ý ÏÈ·¢ËÍ×îµÍλÊý¾Ý ÇÒÑÓʱ ҪСÓÚ4.7us
void iicwritebyte(unsigned char data)
{
unsigned char t;
TM1637_DIO_OUT();
TM1637_CLK=0;
for (t=0;t<8;t++)
{
TM1637_DIO=(data&0x01);
data>>=1;
delay_us(4);
TM1637_CLK=1;
delay_us(4);
TM1637_CLK=0;
delay_us(4);
}
}
u8 iicwaitack(void)
{
u8 ack=0;
TM1637_DIO_IN();
TM1637_CLK=1;
TM1637_DIO=1;
delay_us(4);
if(TM1637_DIO)
{
ack=1;
}
else{
ack=0;
}
TM1637_CLK=0;
delay_us(4);
return ack;
}
//²úÉúackÓ¦´ð
void iicack(void)
{
TM1637_DIO_OUT();
TM1637_CLK=0;
TM1637_DIO=0;
delay_us(2);
TM1637_CLK=1;
delay_us(2);
TM1637_CLK=0;
}
void iicnack(void)
{
TM1637_DIO_OUT();
TM1637_CLK=0;
TM1637_DIO=1;
delay_us(2);
TM1637_CLK=1;
delay_us(2);
TM1637_CLK=0;
}
u8 iicread(u8 ack)
{
u8 i,data=0;
TM1637_DIO_IN();
TM1637_CLK=0;
delay_us(5);
for(i=7;i>=0;i--)
{
TM1637_CLK=1;
delay_us(5);
data|=(TM1637_DIO)<<i;
TM1637_CLK=0;
delay_us(5);
}
if(ack)
{
iicack();
}else{
iicnack();
}
return data;
}
写完这些就基本上完成了iic的底层逻辑,接下来就按照数码管的硬件手册进行代码编写(我在时间显示部分后半部分的代码中其实少了一点,如果你不加亮度调整你是在房仿真的时候显示不出来的)
void timedisplay(void)
{
unsigned char i;
timestart();
iicwritebyte(0x40);
iicack();
timestop();
timestart();
iicwritebyte(0xC0);
iicack();
for(i=0;i<6;i++)
{
iicwritebyte(displaynum[i]);
iicack();
}
timestop();
}
void TM1637_SetBrightness( unsigned char level )
{
timestart();
iicwritebyte(0x88|level);
iicack();
timestop();
}
到这里就已经完成了,接下来可以自己编写自己所需要显示的内容,我这里就写一个简单的时钟显示。这部分其实可以自己去设计了可以结合一些定时器中断来完成实时的时间显示和计时器等等的功能。自己去扩展就好了。
OLED.C
void xianshitime(int hour,int min)
{
int temp;
if(hour>9)
{
int temp2 = hour;
temp=temp2 % 10;
displaynum[1]=tab[temp];
temp2/=10;
displaynum[0]=tab[temp2];
}
if(hour<10)
{
displaynum[0]=tab[0];
displaynum[1]=tab[hour];
}
if(min>9)
{
int temp2 = min;
temp=temp2 % 10;
displaynum[3]=tab[temp];
temp2/=10;
displaynum[2]=tab[temp2];
}
if(min<10)
{
displaynum[2]=tab[0];
displaynum[3]=tab[min];
}
}
第三部分:主函数
main.c
这里我就以最简单的方式完成了整个项目 主要是为了学习一下i2c的逻辑。
#include "stm32f10x.h"
#include "led.h"
#include "oled.h"
unsigned char tab[] =
{
0x3F,/*0*/
0x06,/*1*/
0x5B,/*2*/
0x4F,/*3*/
0x66,/*4*/
0x6D,/*5*/
0x7D,/*6*/
0x07,/*7*/
0x7F,/*8*/
0x6F,/*9*/
0x77,/*10 A*/
0x7C,/*11 b*/
0x58,/*12 c*/
0x5E,/*13 d*/
0x79,/*14 E*/
0x71,/*15 F*/
0x76,/*16 H*/
0x38,/*17 L*/
0x54,/*18 n*/
0x73,/*19 P*/
0x3E,/*20 U*/
0x00,/*21 ??*/
};//这里是数码管显示字符所对应的数据
unsigned char displaynum[]={0x3F,0x3F,0x3F,0x3F,0x3F};
int heart = 0;
int hour,min;
int main(void)
{
delay_init();
TM1637_Init();
while(1)
{
xianshitime(13,14);
timedisplay();
TM1637_SetBrightness(7);
}
}
第四部分:Proteus的电路仿真
首先创建一个proteus的项目,按照我下图的元器件选择即可:
至此我们的准工作就完成了。
接下来就把我们生成的 HEX 文件导入到单片机中即可;
双击单片机
好的接下来点击最下面的开始按键就可以开始你的仿真了。