本文章基于兆易创新GD32 MCU所提供的2.2.4版本库函数开发
向上代码兼容GD32F450ZGT6中使用
后续项目主要在下面该专栏中发布:
https://blog.csdn.net/qq_62316532/category_12608431.html?spm=1001.2014.3001.5482
感兴趣的点个关注收藏一下吧!
电机驱动开发可以跳转:
GD32F103RCT6/GD32F303RCT6-实战项目-无刷电机驱动(1)_gd32f103rct6例程-CSDN博客
BMS电源系统开发可以跳转:暂未放链接
演示视频:
GY-906+数字万年历演示视频
介绍
从本小节开始将会对常用模块进行组合和小作业测试的方式去进行教学,博客后续发表的方式为:stm32版本+GD32版本
本节讲的是GY906红外测温模块的使用以及数字万年历的使用,其中数字万年历采用定时器计数中断制作。
GY-906 是一种常用的红外温度测量模块,广泛应用于非接触式的温度检测场景。这种模块的核心元件通常是 MLX90614 红外温度传感器芯片,由 Melexis 公司生产。MLX90614 芯片结合了先进的红外传感技术和信号处理电路,能够测量物体的表面温度而无需直接接触。
使用资源介绍
stm32f103c8t6:
两路I2C,一个通用定时器
1.其中PB8、PB9用于I2C和OLED显示屏通讯,用于显示数据信息
2.PB8、PA15用于I2C和GY-906模块通讯获取红外温度值
3.定时器3用于产生500Ms的中断,叠加2次后使得秒数+1,作为万年历的时基。
定时器3初始化:
定时器用于时基的产生,驱动我们本次万年历的时钟基础。
//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //TIM3时钟使能
//定时器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
}
对其进行初始化以产生500Ms的中断:
TIM3_Int_Init(4999,7199);//10Khz的计数频率,计数到5000为500ms
GY-906模块的初始化
参考商家链接:
https://pan.baidu.com/s/1V4IX0PKG8vDOionICghSTwhttps://pan.baidu.com/s/1V4IX0PKG8vDOionICghSTw
理后如下:
GY-906.c文件:
#include "GY-906.h"
/*******************************************************************************
* Function Name : Mlx90614_Configuration
* Description : Mlx90614_Configuration
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void Mlx96014_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//SCL
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;//SDA
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOA,&GPIO_InitStructure);
SDA_H;
SCL_H;
}
/*******************************************************************************
* Function Name : SMBus_StartBit
* Description : 在SMBus上生成START条件
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void SMBus_StartBit(void)
{
SDA_H; // Set SDA line
SMBus_Delay(1); // Wait a few microseconds
SCL_H; // Set SCK line
SMBus_Delay(5); // Generate bus free time between Stop
SDA_L; // Clear SDA line
SMBus_Delay(10); // Hold time after (Repeated) Start
// Condition. After this period, the first clock is generated.
//(Thd:sta=4.0us min)
SCL_L; // Clear SCK line
SMBus_Delay(2); // Wait a few microseconds
}
/*******************************************************************************
* Function Name : SMBus_StopBit
* Description : Generate STOP condition on SMBus
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void SMBus_StopBit(void)
{
SCL_L; // Clear SCK line
SMBus_Delay(5); // Wait a few microseconds
SDA_L; // Clear SDA line
SMBus_Delay(5); // Wait a few microseconds
SCL_H; // Set SCK line
SMBus_Delay(10); // Stop condition setup time(Tsu:sto=4.0us min)
SDA_H; // Set SDA line
}
/*******************************************************************************
* Function Name : SMBus_SendByte
* Description : 在SMBus上发送一个字节
* Input : Tx_buffer
* Output : None
* Return : None
*******************************************************************************/
u8 SMBus_SendByte(u8 Tx_buffer)
{
u8 Bit_counter;
u8 Ack_bit;
u8 bit_out;
for(Bit_counter=8; Bit_counter; Bit_counter--)
{
if (Tx_buffer&0x80)
{
bit_out=1; // If the current bit of Tx_buffer is 1 set bit_out
}
else
{
bit_out=0; // else clear bit_out
}
SMBus_SendBit(bit_out); // Send the current bit on SDA
Tx_buffer<<=1; // Get next bit for checking
}
Ack_bit=SMBus_ReceiveBit(); // Get acknowledgment bit
return Ack_bit;
}
/*******************************************************************************
* Function Name : SMBus_SendBit
* Description : Send a bit on SMBus
* Input : bit_out
* Output : None
* Return : None
*******************************************************************************/
void SMBus_SendBit(u8 bit_out)
{
if(bit_out==0)
{
SDA_L;
}
else
{
SDA_H;
}
SMBus_Delay(2); // Tsu:dat = 250ns minimum
SCL_H; // Set SCK line
SMBus_Delay(10); // High Level of Clock Pulse
SCL_L; // Clear SCK line
SMBus_Delay(10); // Low Level of Clock Pulse
// SMBUS_SDA_H(); // Master release SDA line ,
return;
}
/*******************************************************************************
* Function Name : SMBus_ReceiveBit
* Description : 在SMBus上接收一位
* Input : None
* Output : None
* Return : Ack_bit
*******************************************************************************/
u8 SMBus_ReceiveBit(void)
{
u8 Ack_bit;
SDA_H; //?????????,????
SCL_H; // Set SCL line
SMBus_Delay(2); // High Level of Clock Pulse
if (SMBUS_SDA_PIN)
{
Ack_bit=1;
}
else
{
Ack_bit=0;
}
SCL_L; // Clear SCL line
SMBus_Delay(4); // Low Level of Clock Pulse
return Ack_bit;
}
/*******************************************************************************
* Function Name : SMBus_ReceiveByte
* Description : 在SMBus上接收一个字节
* Input : ack_nack
* Output : None
* Return : RX_buffer
*******************************************************************************/
u8 SMBus_ReceiveByte(u8 ack_nack)
{
u8 RX_buffer;
u8 Bit_Counter;
for(Bit_Counter=8; Bit_Counter; Bit_Counter--)
{
if(SMBus_ReceiveBit()) // Get a bit from the SDA line
{
RX_buffer <<= 1; // If the bit is HIGH save 1 in RX_buffer
RX_buffer |=0x01;
}
else
{
RX_buffer <<= 1; // If the bit is LOW save 0 in RX_buffer
RX_buffer &=0xfe;
}
}
SMBus_SendBit(ack_nack); // Sends acknowledgment bit
return RX_buffer;
}
/*******************************************************************************
* Function Name : SMBus_Delay
* Description : 1us
* Input : time
* Output : None
* Return : None
*******************************************************************************/
void SMBus_Delay(u16 time)
{
u16 i, j;
for (i=0; i<4; i++)
{
for (j=0; j<time; j++);
}
}
/*******************************************************************************
* Function Name : SMBus_ReadMemory
* Description : 从RAM/EEPROM读取数据
* Input : slaveAddress, command
* Output : None
* Return : Data
*******************************************************************************/
u16 SMBus_ReadMemory(u8 slaveAddress, u8 command)
{
u16 data; // Data storage (DataH:DataL)
u8 Pec; // PEC byte storage
u8 DataL=0; // Low data byte storage
u8 DataH=0; // High data byte storage
u8 arr[6]; // Buffer for the sent bytes
u8 PecReg; // Calculated PEC byte storage
u8 ErrorCounter; // Defines the number of the attempts for communication with MLX90614
ErrorCounter=0x00; // Initialising of ErrorCounter
slaveAddress <<= 1; //2-7???????
do
{
repeat:
SMBus_StopBit(); //If slave send NACK stop comunication
--ErrorCounter; //Pre-decrement ErrorCounter
if(!ErrorCounter) //ErrorCounter=0?
{
break; //Yes,go out from do-while{}
}
SMBus_StartBit(); //Start condition
if(SMBus_SendByte(slaveAddress))//Send SlaveAddress ???Wr=0????????
{
goto repeat; //Repeat comunication again
}
if(SMBus_SendByte(command)) //Send command
{
goto repeat; //Repeat comunication again
}
SMBus_StartBit(); //Repeated Start condition
if(SMBus_SendByte(slaveAddress+1)) //Send SlaveAddress ???Rd=1????????
{
goto repeat; //Repeat comunication again
}
DataL = SMBus_ReceiveByte(ACK); //Read low data,master must send ACK
DataH = SMBus_ReceiveByte(ACK); //Read high data,master must send ACK
Pec = SMBus_ReceiveByte(NACK); //Read PEC byte, master must send NACK
SMBus_StopBit(); //Stop condition
arr[5] = slaveAddress;
arr[4] = command;
arr[3] = slaveAddress+1; //Load array arr
arr[2] = DataL;
arr[1] = DataH;
arr[0] = 0;
PecReg=PEC_Calculation(arr); //Calculate CRC
}
while(PecReg != Pec); //If received and calculated CRC are equal go out from do-while{}
data = (DataH<<8) | DataL; //data=DataH:DataL
return data;
}
/*******************************************************************************
* Function Name : PEC_calculation
* Description : 计算接收字节的PEC
* Input : pec[]
* Output : None
* Return : pec[0]-this byte contains calculated crc value
*******************************************************************************/
u8 PEC_Calculation(u8 pec[])
{
u8 crc[6];
u8 BitPosition=47;
u8 shift;
u8 i;
u8 j;
u8 temp;
do
{
/*Load pattern value 0x000000000107*/
crc[5]=0;
crc[4]=0;
crc[3]=0;
crc[2]=0;
crc[1]=0x01;
crc[0]=0x07;
/*Set maximum bit position at 47 ( six bytes byte5...byte0,MSbit=47)*/
BitPosition=47;
/*Set shift position at 0*/
shift=0;
/*Find first "1" in the transmited message beginning from the MSByte byte5*/
i=5;
j=0;
while((pec[i]&(0x80>>j))==0 && i>0)
{
BitPosition--;
if(j<7)
{
j++;
}
else
{
j=0x00;
i--;
}
}/*End of while */
/*Get shift value for pattern value*/
shift=BitPosition-8;
/*Shift pattern value */
while(shift)
{
for(i=5; i<0xFF; i--)
{
if((crc[i-1]&0x80) && (i>0))
{
temp=1;
}
else
{
temp=0;
}
crc[i]<<=1;
crc[i]+=temp;
}/*End of for*/
shift--;
}/*End of while*/
/*Exclusive OR between pec and crc*/
for(i=0; i<=5; i++)
{
pec[i] ^=crc[i];
}/*End of for*/
}
while(BitPosition>8); /*End of do-while*/
return pec[0];
}
/*******************************************************************************
* Function Name : SMBus_ReadTemp
* Description : 计算并返回温度
* Input : None
* Output : None
* Return : SMBus_ReadMemory(0x00, 0x07)*0.02-273.15
*******************************************************************************/
float SMBus_ReadTemp(void)
{
return SMBus_ReadMemory(SA, RAM_ACCESS|RAM_TOBJ1)*0.02-273.15;//绝对零度
}
/*********************************END OF FILE*********************************/
在主函数中进行初始化:
Mlx96014_Configuration();
万年历设计:
首先定义本次使用到的变量:
u8 sec = 0;
u8 min = 10;
u8 hour=23;
u8 date = 19;
u8 month = 6;
u16 year = 2024;
u8 day_max=31;
其中day_max为最大天数,用于确定当前月份的最大天数是多少
判断内容如下:
if(month==2)
{
day_max=28;
}
else if(month==4||month==6||month==9||month==11)
{
day_max=30;
}
else
{
day_max=31;
}
根据不同月份,去对天数进行判断,
一年一共有365天或者366天,平年有365天,闰年有366天,闰年每隔4年一次。
平年的2月是28天,闰年2月是29天。
4月、6月、9月、11月各是30天。
1月、3月、5月、7月、8月、10月、12月各是31天。
接下来就是在定时器中断函数中对万年历的基础代码进行编写:
if(i==2)
{
i=0;
sec++;
if(sec>=60)
{
sec=0;
min++;
if(min>=60)
{
min=0;
hour++;
if(hour>=24)
{
hour=0;
date++;
if(date>=day_max)
{
date=0;
month++;
if(month>=12)
{
year++;
}
}
}
}
}
}
在定时器500Ms的中断时间作为基础时间基数的情况下,通过秒数的累加,去驱动整个万年历进行工作
主函数
主函数中要做的就很简单,因为逻辑代码在中断里面已经做完了,所以主函数中只需要完成OLED的显示和基础功能的初始化即可!
OLED_ShowNum( 5, 15,year,4,12);
OLED_ShowString(30, 15,"/",12);//6*12 “ABC”
OLED_ShowNum( 35, 15,month,2,12);
OLED_ShowString(50, 15,"/",12);//6*12 “ABC”
OLED_ShowNum( 55, 15,date,2,12);
OLED_ShowNum( 15, 25,hour,2,12);
OLED_ShowString(30, 25,"/",12);//6*12 “ABC”
OLED_ShowNum( 35, 25,min,2,12);
OLED_ShowString( 50, 25,"/",12);//6*12 “ABC”
OLED_ShowNum( 55, 25,sec,2,12);
OLED_ShowString(0,0,Temperatures,16);//6*12 “ABC”
OLED_Refresh();
总结:
经过本小结,希望大家能够掌握GY-906的使用,以及用stm32所提供的定时器去完成对万年历的设计!