在线时间234 小时
UID3301905
注册时间2017-1-8
NXP金币172
TA的每日心情 | 开心 2018-4-20 15:04 |
---|
签到天数: 8 天 连续签到: 1 天 [LV.3]偶尔看看II
金牌会员
 
- 积分
- 3265
- 最后登录
- 2023-7-24
|
【飞凌RT1052】SYSTICK计时器&DHT11温湿度传感器&RTC&TRNG综合实验
本文涉及到外设:SYSTICK,LPI2C,TRNG。
绝大部分的传感器都是串行通信,而串行通信根据协议又分为单总线 I2C SPI UART四种,而异步单串行总线和I2C总线通信在单片机项目中用得非常多,加上NXP本身拥有国际标准I2C通信协议的修改、发布、推广权,因此NXP的单片机中都集成了非常好用的硬件LPI2C接口,可根据I2C从机的情况修改通信速率、数据位、有无ack握手信令等参数。
在RT1052的官方SDK中,LPI2C例程是针对板上的RTC即实时日历时钟芯片RX8010和一片EEPROM,EEPROM不做多余研究,只研究项目中需要用到的RX8010芯片:
只需要做出LPI2C的初始化之后就可以用这个接口了:
lpi2c_master_config_t masterConfig = {0};
LPI2C_MasterGetDefaultConfig(&masterConfig);
masterConfig.baudRate_Hz = baud;
LPI2C_MasterInit(LPI2C1, &masterConfig,CLOCK_GetFreq(kCLOCK_Usb1PllClk)/48);
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL,1);
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA,1);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL,0xD8B0u);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA,0xD8B0u);
单片机设置LPI2C模式为主机模式,设置LPI2C时钟为USBPLL时钟的48分频,引脚复用号为1,配置模式为0xD8B0u,即开漏。
LPI2C读写传输的函数就更简单了:
status_t LPI2C1_Write(LPI2C_Type *base,unsigned char addr,int subAdd,unsigned char *dataBuff,uint16_t dataLen)
{
lpi2c_master_transfer_t xfer;
status_t status;
xfer.slaveAddress = addr;
xfer.direction = kLPI2C_Write;
xfer.subaddress = subAdd;
xfer.subaddressSize = 0x01;
xfer.data = dataBuff;
xfer.dataSize = dataLen;
xfer.flags = kLPI2C_TransferDefaultFlag;
status = LPI2C_MasterTransferBlocking(base,&xfer);
return status;
}
int LPI2C1_Read(LPI2C_Type *base,unsigned char addr,int subAdd,unsigned char* dataBuffer, uint16_t dataLen)
{
lpi2c_master_transfer_t masterXfer = {0};
status_t reVal = kStatus_Fail;
masterXfer.slaveAddress = addr;
masterXfer.direction = kLPI2C_Read;
masterXfer.subaddress = subAdd;
masterXfer.subaddressSize = 0x01;
masterXfer.data = dataBuffer;
masterXfer.dataSize = dataLen;
masterXfer.flags = kLPI2C_TransferDefaultFlag;
reVal =LPI2C_MasterTransferBlocking(base,&masterXfer);
if (reVal!=kStatus_Success)
return 1;
return 0;
}
只需要指定从机地址slaveAddress,读写寄存器起始地址subaddress,读写方向direction,读写长度subaddressSize,然后传入unsigned char类型的数组作为读写数据载体就完事了,非常简单。
由于官方操作RX8010的函数可读性不高,因此我封装成了直接读写年、月、日、时、分、秒的函数,以形参列表按序传入即可,这种所有操作封装起来的函数,比较符合我的代码风格:
int RX8010_Set_Time(unsigned char year,unsigned char month,unsigned char date,unsigned char hour,
unsigned char minute,unsigned char sec)
{
unsigned char y,m,d,h,mi,s;
y=Byte_2_BCD(year/10,year%10);
LPI2C1_Write(LPI2C1,RX8010_ADDR,RX8010_YEAR,&y,1);
if(month<1||month>12)
return 1;
m=Byte_2_BCD(month/10,month%10);
LPI2C1_Write(LPI2C1,RX8010_ADDR,RX8010_MONTH,&m,1);
if(date<1||date>31)
return 2;
d=Byte_2_BCD(date/10,date%10);
LPI2C1_Write(LPI2C1,RX8010_ADDR,RX8010_MDAY,&d,1);
if(hour<0||hour>24)
return 3;
h=Byte_2_BCD(hour/10,hour%10);
LPI2C1_Write(LPI2C1,RX8010_ADDR,RX8010_HOUR,&h,1);
if(minute<0||minute>60)
return 4;
mi=Byte_2_BCD(minute/10,minute%10);
LPI2C1_Write(LPI2C1,RX8010_ADDR,RX8010_MIN,&mi,1);
if(sec<0||sec>60)
return 5;
s=Byte_2_BCD(sec/10,sec%10);
LPI2C1_Write(LPI2C1,RX8010_ADDR,RX8010_SEC,&s,1);
return 0;
}
int RX8010_Get_Time()
{
unsigned char date[7],dateRsul[7],flagreg;
LPI2C1_Read(LPI2C1,RX8010_ADDR,RX8010_FLAG,&flagreg,1);
if (flagreg & RX8010_FLAG_VLF)
{
return 1;
}
LPI2C1_Read(LPI2C1,RX8010_ADDR,RX8010_SEC,date,7);
dateRsul[0] = BCD_2_Byte(date[RX8010_SEC - RX8010_SEC] & 0x7f);
dateRsul[1] = BCD_2_Byte(date[RX8010_MIN - RX8010_SEC] & 0x7f);
dateRsul[2] = BCD_2_Byte(date[RX8010_HOUR - RX8010_SEC] & 0x3f);
dateRsul[4] = BCD_2_Byte(date[RX8010_MDAY - RX8010_SEC] & 0x3f);
dateRsul[5] = BCD_2_Byte(date[RX8010_MONTH - RX8010_SEC] & 0x1f);
dateRsul[6] = BCD_2_Byte(date[RX8010_YEAR - RX8010_SEC]);
dateRsul[3] = date[RX8010_WDAY - RX8010_SEC] & 0x7f;
printf("\r\n Read datetime from RX8010: 20%d%d-%d%d-%d%d %d%d:%d%d:%d%d \r\n",
dateRsul[6]/10,dateRsul[6]%10,dateRsul[5]/10,dateRsul[5]%10,dateRsul[4]/10,dateRsul[4]%10,
dateRsul[2]/10,dateRsul[2]%10,dateRsul[1]/10,dateRsul[1]%10,dateRsul[0]/10,dateRsul[0]%10);
return 0;
}
在主程序中初始化一个确定的时间即可开始循环采集:
LPI2C1_Init(100000);
RX8010_Set_Time(18,11,25,0,23,0);
while(1)
{
...
if(RX8010_Get_Time()==0)
printf("20%d-%d-%d %d:%d:%d\n\n",year,month,mdate,hour,min,sec);
...
}
然后是DHT11,要使用DHT11必须先有精确延时微秒的函数,此处使用SYSTICK计时器实现阻塞延时:
int fac_us;
void Delay_Init(int sysclk)
{
fac_us=sysclk;
SysTick->CTRL|=SysTick_CTRL_CLKSOURCE_Msk;
SysTick->LOAD=16777215;
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;
}
设置系统计数值600,即主频。
void Delay_us(int nus)
{
int ticks,told,tnow,tcnt=0,reload=SysTick->LOAD;
ticks=nus*fac_us;
told=SysTick->VAL;
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow;
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break;
}
}
}
此处是使用阻塞延时方式。
连接DHT11到板上的GPIO1_24引脚,即8针KEY接口的第一针,供电采用SWD调试接口那边的3V3:
然后参考网上的DHT11例程,配置好GPIO之后即可使用:
unsigned char DHT11_Read_Data(unsigned char *temp,unsigned char *humi)
{
unsigned char buf[5];
unsigned char i;
DHT11_Rst();
if(DHT11_Check()==0)
{
for(i=0;i<5;i++)
{
buf=DHT11_Read_Byte();
}
if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
{
*humi=buf[0];
*temp=buf[2];
}
}
else return 1;
return 0;
}
最后是随机数发生器TRNG,这个我只是用来测试的,后面的项目会用到,随机生成480个【0,271】范围内的随机正整数并在RGB液晶屏上显示出来:
int a[480];
trng_config_t trngConfig;
TRNG_GetDefaultConfig(&trngConfig);
trngConfig.sampleMode = kTRNG_SampleModeVonNeumann;
TRNG_Init(TRNG,&trngConfig);
TRNG_GetRandomData(TRNG,a,480);
for(i=0;i<480;i++)
{
if(a<0)a=-a;
a=a%(272-1+1)+1;
ELCDIF_RGB_DrawVLine(i,272-a,a,0xffff);
}
观察效果:
工程文件:
工程.zip
(1.24 MB, 下载次数: 6)
|
|