复旦微MCU学习笔记
- 写在前面
- 准备工作
- 外设清单
-
- 1.UART串口
-
- 串口对应引脚
- 2.ADC
-
- 采样通道对应引脚
- 项目功能
-
- 1.ADC采样(以MQ-2为例)
- 2.单总线通信(以DS18B20为例)
-
- ①STM32F103C8T6代码调试
-
- 1.用于传感器的引脚初始化:
- 2.传感器复位检查(检查是否有传感器)
- 3.启用传感器温度转换
- 4.读数
- ②FM33LC046N代码移植
写在前面
??1.帖子原因:由于巧合,使用复旦微芯片嵌入式设计,复旦微论坛有很多信息也有很多沟通帖子,但他们的发展会遇到很多问题,帖子是记录他们的学习过程,也方便检查相关信息,目的是学习使用。 ??2.芯片选择: ??①之前参加过复旦微电赛,比赛推荐的芯片是FM33LG048,可惜当时市场上没有可靠的官方开发板,所以选择了FM33LC046N复旦微代码逻辑首先适应。现在开发下一个项目涉及芯片的选择,手头正好有FM33LC046N所以最后选择了开发板FM33LC046N(市场似乎已经存在了FM33LG该系列开发板已售出,可根据需要选择,不必选择LC系列)。 ??②此外,我以前参加过嵌入式比赛,赞助商也参加过比赛RTThread,这是一个国内嵌入式操作系统,配备了独立编程的软件,感觉更方便,由于时间和精力有限,不能深入学习操作系统,在其支持RTThread Studio复旦微板卡在软件中还没有找到,也许双方还没有正式合作,但在复旦微论坛上,有人加入了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个外部采样通道和4个内部特殊通道(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;
}