复旦微MCU学习笔记
写在前面
1.立帖原因:因机缘巧合吧,用到了复旦微的芯片进行嵌入式设计,复旦微论坛上有很多资料也有很多交流的帖子,但是自己开发的时候还是会遇到很多问题,这个帖子是记录自己学习历程的,也方便自己查相关资料,宗旨是用到什么就学什么。
2.芯片选择:
①之前参加过复旦微的电赛,大赛推荐的芯片是FM33LG048,但是可惜当时市面上没有靠谱的官方制定的开发板在售,所以选用了FM33LC046N先适应的复旦微的代码逻辑。现在在开发下一个项目的时候涉及到选择芯片,手头上正好有FM33LC046N的开发板,所以最终选择了FM33LC046N(市面上好像已经有FM33LG系列的开发板在售了,可以根据需要选择,不是非要选择LC系列)。
②此外之前参加过嵌入式比赛,大赛赞助商有RTThread,这是一个国产的嵌入式操作系统,有其配备的软件独立编程,感觉上还是比较方便的,因时间精力有限可惜不能深入学习该操作系统,在其配套的RTThread Studio软件内也没有找到复旦微的板卡,可能双方还没有正式i合作吧,但是在复旦微的论坛上有人给出了加入了RTThread操作系统的LC系列板卡的bsp包,也就意味着,LC的板子上是可以跑RTThread操作系统的,虽然因为时间精力问题,没法真的将国产操作系统投入应用,但是选择LC系列的开发板也算是留有了开发空间。
准备工作
FM33LC046N开发板有很多排针引出来(开发板都这样),但是改开发板通过Type-B串口实现5V供电,板载线性降压芯片降压至3.3V,有几个跳线帽必须接才能实现上述功能,即实现板卡的正常供电:
此外我们调试常用到的就是串口和LED灯,串口后面再说,LED这里也需要接上一个端子:
对应的实物上就是用跳线帽接上这几个位置:
有点看不清,在板子的左侧,图中用白色圆圈圈出来了。
外设清单
1.UART串口
串口对应引脚
引脚 | UART | 符号 | 功能 |
---|---|---|---|
PA2、PA13 | UART0 | UART0_RX | 数据接收 |
PA3、PA14 | UART0_TX | 数据发送 | |
PC2、PB13 | UART1 | UART1_RX | 数据接收 |
PC3、PB14 | UART1_TX | 数据发送 | |
PA0、PB2 | UART4 | UART4_RX | 数据接收 |
PA1、PB3 | UART4_TX | 数据发送 | |
PC4、PD0 | UART5 | UART5_RX | 数据接收 |
PC5、PD1 | UART5_TX | 数据发送 |
2.ADC
采样通道对应引脚
FM33LC046N共有12个外部采样通道和四个内部特殊通道(TS、Vref、OPA1、OPA2)
通道 | 引脚 | 说明 |
---|---|---|
ADC_IN0 | PC9 | 快速通道 |
ADC_IN1 | PC10 | 快速通道 |
ADC_IN2 | PD11 | 快速通道 |
ADC_IN3 | PD0 | 快速通道 |
ADC_IN4 | PD1 | 快速通道 |
ADC_IN5 | PD2 | 快速通道 |
ADC_IN6 | PA13 | 快速通道 |
ADC_IN7 | PA14 | 快速通道 |
ADC_IN8 | PC7 | 慢速通道 |
ADC_IN9 | PC8 | 慢速通道 |
ADC_IN10 | PA15 | 慢速通道 |
ADC_IN11 | PC6 | 慢速通道 |
项目功能
1.ADC采样(以MQ-2为例)
硬件接法:
1.MQ-2传感器
①5V引脚接板子上的VCC5(Type-B接口附近,接跳线帽上面的排针);
②AO引脚接开发板PC9引脚;
③GND接开发板GND
2.USB-TTL(串口)
接开发板UART0(PA13接TXD,PA14接RXD),串口5V引脚悬空,3.3V引脚跳线帽短接VCC。
代码存放目录(本地电脑):
E:\MCU\FM33\Jiedian\ADC
用的传感器是MQ-2,就是最基础的烟雾传感器(对可燃气也有反应),查资料显示FM33LC046N好像只有一个1Msps 12bit的ADC,烧录程序后测试,正常情况下,电压表实测400mA,采样串口输出406mA,使用打火机测试(丁烷气体)后,AO采样电压升高,由于动态变化比较难读数,测试结果如下表所示:
实测值(mA) | 采样值(mA) |
---|---|
400 | 406 |
441 | 449 |
953 | 961 |
1492 | 1518 |
1555 | 1560 |
整体误差可以说是非常小了,误差基本在10mA以内,上表还包含了人眼测量的误差,可以说表现让人很满意。
有兴趣的可以去试一下用STM32F103C8T6芯片去试一下,例程很多,我测试过了,包括所谓的ADC上电校准,包括Vref校准在内我都试过了,低电压的时候误差在100mA左右,高电压的时候的误差简直不能看。不是针对最小系统板,懂得都懂,STM32F103C8T6功能很强大,资料也很多,是我嵌入式入门的板子,就事论事,这个ADC采样是真的拉跨,网上有人说对精度有要求的话可以外接ADC,我比较懒……
2.单总线通信(以DS18B20为例)
本来以为这是很简单的事,没想到这反而是最让我头疼的,下面记录一下调试步骤:
①STM32F103C8T6代码调试
众所周知,STM32的最大优势是它资料多,DS18B20的代码直接就有现成的,可以参考正点原子的代码即可,我这边整理一下主要步骤:
1.传感器用到的引脚初始化:
这里使能的PB0引脚,初始化为推挽输出模式,初始化完成之后引脚置高电平:
2.传感器复位检验(检查有无传感器)
①传感器复位
//复位DS18B20
void DS18B20_Rst(void)
{
DS18B20_IO_OUT(); //SET PB0 OUTPUT
DS18B20_DQ_OUT=0; //拉低DQ
delay_us(750); //拉低750us
DS18B20_DQ_OUT=1; //DQ=1
delay_us(15); //15US
}
这里的DS18B20_IO_OUT和后面遇到的DS18B20_IO_IN是STM32特有的位带操作,OUT就是把对应的引脚切换为开漏输出模式,IN就是把对应的引脚切换为上拉/下拉输入模式:
//IO方向设置
#define DS18B20_IO_IN() {GPIOB->CRL&=0XFFFFFFF0;GPIOB->CRL|=8<<0;}
#define DS18B20_IO_OUT() {GPIOB->CRL&=0XFFFFFFF0;GPIOB->CRL|=3<<0;}
CRL低四位赋值8即如下图(1 0 0 0):
CRL低四位赋值3即如下图所示(0 1 1 0):
根据下图手册中的定义可以解读出配置的对应模式是什么,前文写了,分别是开漏输出模式和上拉/下拉输入模式。
这里因为使用的是PB0引脚所以是CRL,如果是8~15引脚,对应的CRH寄存器,原理都是一样的。
②传感器检查
根据DS18B20官方手册中所示,传感器在接收到MCU发送的初始化信号后,会拉低60~240us,然后拉高。
因此,代码中先将PB0置为上拉输入模式,然后读取PB0引脚的电平,如果没接传感器,那么引脚在电阻拉高之后将不会被拉低,代码将会在第一个while循环内循环至retry=200,最终返回1,表示未检测到DS18B20传感器;在检测到第一阶段的低电平之后,传感器会再次拉高,代码中通过第二个while循环检测,当传感器并没有拉高时,retry会循环累加至240,同样返回1,表示未检测到传感器,反之则表示传感器初始化成功。
//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
u8 DS18B20_Check(void)
{
u8 retry=0;
DS18B20_IO_IN(); //SET PB0 INPUT
while (DS18B20_DQ_IN&&retry<200)
{
retry++;
delay_us(1);
};
if(retry>=200)return 1;
else retry=0;
while (!DS18B20_DQ_IN&&retry<240)
{
retry++;
delay_us(1);
};
if(retry>=240)return 1;
return 0;
}
3.启用传感器温度转换
以上是相当于传感器初始化,后面的流程(即步骤3、4)每进行一次读取温度,都需要执行,因此放在了while主循环内。
//开始温度转换
void DS18B20_Start(void)
{
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc); // skip rom
DS18B20_Write_Byte(0x44); // convert
}
这里可以看出来前面依然是初始化流程,不同的是发送了0xcc和0x44至传感器,作用是启动温度转换:
DS18B20传感器的手册中说明了,0xcc指令的作用就是同时寻址总线上所有设备,后半句说了,主机在0xcc指令后接一个0x44指令可以使总线上的所有DS18B20传感器同时执行温度转换。
Write_Byte函数如下所示,需要结合DS18B20传感器读写时序图看:首先将引脚配置为开漏输出模式,取需要发送的数据的最低位赋值给testb,如果是1,先拉低总线电平>1us,然后拉高,如果是0,拉低最大值60us,然后拉高:
//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(u8 dat)
{
u8 j;
u8 testb;
DS18B20_IO_OUT(); //SET PB0 OUTPUT;
for (j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if (testb)
{
DS18B20_DQ_OUT=0; // Write 1
delay_us(2);
DS18B20_DQ_OUT=1;
delay_us(60);
}
else
{
DS18B20_DQ_OUT=0; // Write 0
delay_us(60);
DS18B20_DQ_OUT=1;
delay_us(2);
}
}
}
DS18B20传感器读写时序图如下所示:
4.读数
首先还是初始化流程,然后发送了0xcc、0xbe指令,然后就是MCU读取存放了DS18B20温度数据的寄存器:
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc); // skip rom
DS18B20_Write_Byte(0xbe); // convert
TL=DS18B20_Read_Byte(); // LSB
TH=DS18B20_Read_Byte(); // MSB
这里我一开始很好奇,为什么每次都要先初始化一下,初始化之后温度转换的数据是不是就没了?然后在传感器说明书中找到了如下说明,意思就是,主机每次需要和DS18B20通信发送相应指令时都需要执行一次初始化流程:
0xbe指令的作用是读取暂存器中的数据,数据传输从最低有效位0开始,直到第九个字节被读取。
此外需要注意只有在总线上只有一个DS18B20传感器的时候,0xcc指令后面可以直接跟一个0xbe指令。
Read_Byte函数如下所示,就是简单的一位一位读取然后赋值给dat:
//从DS18B20读取一个字节
//返回值:读到的数据
u8 DS18B20_Read_Byte(void)
{
u8 i,j,dat;
dat=0;
for (i=1;i<=8;i++)
{
j=DS18B20_Read_Bit();
dat=(j<<7)|(dat>>1);
}
return dat;
}
Read_Bit函数如下所示,这里就需要结合传感器手册给出的时序图来看了,首先读取数据需要先将总线拉低至少1us,这里先将引脚配置为开漏输出,然后拉低总线2us,然后恢复总线至高电平。
这时候如果DS18B20传感器是数据是0,DS18B20会将总线拉低最后再拉高,否则会使总线一直保持为高。
因此先将引脚配置为上拉输入模式,延时12us之后(这里为什么是12us呢,大概就是下图中主机发送指令的时长为15us,前面拉低延时过2us了,这里设置为12us),然后读取总线数据,如果为低电平就是0,反之就是1:
//从DS18B20读取一个位
//返回值:1/0
u8 DS18B20_Read_Bit(void)
{
u8 data;
DS18B20_IO_OUT(); //SET PB0 OUTPUT
DS18B20_DQ_OUT=0;
delay_us(2);
DS18B20_DQ_OUT=1;
DS18B20_IO_IN(); //SET PB0 INPUT
delay_us(12);
if(DS18B20_DQ_IN)data=1;
else data=0;
delay_us(50);
return data;
}
②FM33LC046N代码移植
代码移植的过程中主要遇到的问题就是DS18B20是1-wire通信,对时序的要求非常高,而复旦微微秒级别的定时不准,用了高级定时器也未能解决问题,当时用示波器抓过波形,仔细分析过抓取的时序波形的延时时间发现与代码中写的出入较大,导致从DS18B20读取的数据一直是错的。当时抓的示波器波形图没有保存,如果大家也尝试过这一步应该也能发现同样的问题。我看评论区有人与复旦微技术支持沟通后解决了该问题,确实很厉害,我当时受项目时间的限制,来不及慢慢研究解决办法,直接用的MCU的内部温度传感器获取温度数据了,毕竟我需要获取的也是芯片周围的温度来测试环境温度升高到多少时芯片停止工作以便设置报警阈值。
写在最后
首先道个歉吧,给各位期待这篇博文继续更新的学友门,也给停更了这么久的自己。先说明一下原因,当时比赛结束之后有太多事情要忙了:
1.先是发现赛题要求用FM33LG系列的芯片,换了芯片之后对代码进行了修改,然后进了复赛答辩,然后决赛答辩,在这个过程中不断完善系统,丰富功能,最终拿下了当届的软硬件赛道全国一等奖,中间一直没有更新博文。
2.之后还面临着毕业找工作的事情,可以说是很重要的事情了,当时盛传是就业寒冬,所以为了面试做了蛮多准备的,也没心情更新博文。
3.再之后就是要忙大论文,要应对严格的盲审,毕不了业找到的工作也就没了,论文质量不行毕业后也可能被撤销学位,总之鸭梨山大。
4.再之后盲审过了之后要准备答辩、毕业材料、离校手续等等,繁琐的事情居多。
5.毕业之后进公司,试用期压力很大,整天都处于认真学习公司产品,提升业务技能的状态,也没有什么心情更新博文。
6.转正之后有时间来思考今后的职业生涯规划问题了,再打开每天都会用到的CSDN,才发现自己当初写的博文还是有人在看的,多多少少也算是帮到了大家,如今因为工作关系,暂时不用复旦微的芯片了,所以这篇博文暂时也不会再更新了,代码移植的逻辑都是相通的,动手做多了就能明白,甚至有些国产芯片不需要移植,直接把STM32的代码拿来就能用。之前因为公司保密问题,所以基本不更新博文,今后会自己做一些小项目,记录和分享一下经验,当然也不会泄公司的密,不然饭碗没咯哈哈哈。
最后的最后,真的很感谢大家,是你们的点赞、评论、关注和分享让我觉得嵌入式路上有很多朋友同行,也让我有决心重新回到分享嵌入式开发经验的路上。