上一节介绍了无锡学蠡信息科技有限公司的无线传感器网络实验平台关于STM32F103单片机的基础实验,这一章,主要是使用STM32对单个传感器或执行器模块的单个实验进行介绍和演示。
无线传感器网络实验平台提供的实验代码在senior_main.c文件中为所有的传感器和执行器都编写了独立的.c文件以进行传感器初始化、模块类型识别、GPIO初始化操作等对每个传感器模块进行底层硬件操作,同时为了可以统一的管理所有的传感器模块,在main.h中建立了结构体,以实现一个工程代码可以实现所有传感器和执行器模块的功能演示,只要更换传感器模块直接复位即可切换不同传感器模块的对应程序切换,避免了不同模块需要烧录不同程序的不便。
在无线传感器网络实验平台中,为了可以实现传感器模块插拔后能够自动识别传感器模块的类型,实验平台对每个类型的传感器模块都设计有不同的两路分压电路,使用STM32读取分压电路的两端电压值,因为每个分压电路所使用的电阻其电阻值是不同的,所以内部设定的分压电路的两端电压值也是不同的,所以在进行传感器模块的插拔更换后,MCU只要读取分压电路两端的电压值再与预先设定好的值进行比较,就可以实现传感器类型自动识别。
分压电路(之一)原理图:
在main.h文件中,建立了一个结构体,用于存放传感器类型序列 id, 用于识别区分传感器模块的分压电路的PA4、PA5的电压值, 传感器类型名以及传感器对应的处理功能函数。
结构体如下:
typedef struct
{
uint16_t id;
//传感器类型序号
uint16_t boardpa4voltage; //PA4 引脚电压值
uint16_t boardpa6voltage; //PA6 引脚电压值
char *boardtype;
//传感器类型名
void (*func) (void);
//传感器处理函数
}Sensortype_t;
实际定义的结构体变量写在main.c文件中,具体代码如下:
Sensortype_t sensortype[SENSORTYPE_NUM] =
{
{1, 0 , 290, " 8x8Led ", Led8x8_main},
{2, 0 , 2200, " Infrared ", Infrared_main},
{3, 0 , 540, " 4Led ", Led4_main},
{4, 0 , 1620, " 2Realy ", Realy2_main},
{5, 3300, 1620, " Hall ", Hall_main},
{6, 3300, 1920, " LED-Light ", Ledlight_main},
{7, 0 , 1035, "BEEP-POR-TEM", BeepPortemp_main},
{8, 0 , 2440, " Shack ", Shack_main},
{9, 3300, 2245, " Flame ", Flame_main},
{10, 0 , 1310, " Pressure ", Pressure_main},
{11, 0 , 800, " Humidity ", SHT10_main},
{12, 3300, 290, " Gas ", Gas_main},
{13, 0 , 2690, " Acc ", Acc_main},
{14, 3300, 1035, " Ulrasonic ", Ultrasonic_main}
};
实验平台通过ADC采集函数——BoardPAXMeasureVolage,采集分压电路 PA4 PA6 引脚电压值,然后在循环中不断和传感器结构体变量sensortype预存的值进行对比,通过变量i来进行传感器类型序号的记录,对比成功则break跳出循环,跳出循环时的i值就是对应在使用的传感器类型序号。
实现代码如下:
while(adccnt++ < 10)
{
adctotal1 += BoardPAXMeasureVolage(&hadc1, ADC_CHANNEL_4);
adctotal2 += BoardPAXMeasureVolage(&hadc1, ADC_CHANNEL_6);
}
BoardPA4Voltage = adctotal1 / 10;
BoardPA6Voltage = adctotal2 / 10;
{
uint8_t i = 0;
for(i = 0; i < SENSORTYPE_NUM; i++)
{
if(sensortype[i].boardpa4voltage == 0)
{
if((BoardPA4Voltage < sensortype[i].boardpa4voltage + 200) &&
(BoardPA6Voltage > sensortype[i].boardpa6voltage - 200) &&
(BoardPA6Voltage < sensortype[i].boardpa6voltage + 200))
{
break;
}
}
else if(sensortype[i].boardpa4voltage == 3300)
{
if((BoardPA4Voltage > sensortype[i].boardpa4voltage - 200) &&
(BoardPA6Voltage > sensortype[i].boardpa6voltage - 200) &&
(BoardPA6Voltage < sensortype[i].boardpa6voltage + 200))
{
break;
}
}
}
}
通过以上的分压电路和对应的软件代码,就能实现传感器模块类型的上电自动识别功能,接下来就开始进行单个传感器模块的实验。
完成数字取模
使用取模软件,安装点阵屏大小以及你实际想要显示的字体大小进行数字取模,在本实验中使用的字模设置为阳码、逐行扫描、顺向。设置完成后就会自动生成对应的字模数组。
初始化GPIO口
void LED8X8Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = HC595_SER_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(HC595_SER_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = HC595_RCLK_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(HC595_RCLK_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = HC595_SCK_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(HC595_SCK_PORT, &GPIO_InitStruct);
GpioWrite(HC595_SER_PORT,HC595_SER_PIN,GPIO_PIN_RESET);
GpioWrite(HC595_RCLK_PORT,HC595_RCLK_PIN,GPIO_PIN_RESET);
GpioWrite(HC595_SCK_PORT,HC595_SCK_PIN,GPIO_PIN_RESET);
Write595(0X00);
Write595(0xff);
GpioWrite(HC595_SER_PORT,HC595_SER_PIN,GPIO_PIN_SET);
GpioWrite(HC595_SER_PORT,HC595_SER_PIN,GPIO_PIN_RESET);//重启时无显示
}
完成数据显示
调用 SHT10_ LED8X8Init 初始化函数之后,进入 while(1)循环,不停的向 74HC595 发送字模
数据,显示数字。
void Led8x8_main(void)
{
static unsigned char disnum=5;
LED8X8Init();
while(1)
{
static unsigned char i;
Write595(FontSystem8x8[disnum*8+i]);
Write595(~DispWm);
GpioWrite(HC595_RCLK_PORT,HC595_RCLK_PIN,GPIO_PIN_SET);
GpioWrite(HC595_RCLK_PORT,HC595_RCLK_PIN,GPIO_PIN_RESET);//重启时无显示
DispWm <<= 1;
if(++i == 8)
{
DispWm = 1;
i = 0;
}
}
}
void SHT10_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = SHT10_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(SHT10_SDA_PORT, &GPIO_InitStruct);
GpioWrite(SHT10_SDA_PORT,SHT10_SDA_PIN,GPIO_PIN_SET);
GPIO_InitStruct.Pin = SHT10_SCK_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(SHT10_SCK_PORT, &GPIO_InitStruct);
GpioWrite(SHT10_SCK_PORT,SHT10_SCK_PIN,GPIO_PIN_SET);
SHT10_ConReset(); //复位通讯
}
void SHT10_main(void)
{
uint16_t humi_val, temp_val;
uint8_t err = 0, checksum = 0;
float temp_val_real=0.0;
float humi_val_real=0.0;
// float dew_point=0.0;
char tempBuf[12];
SHT10_Config();
while(1)
{
err += SHT10_Measure(&temp_val, &checksum, TEMP);
err += SHT10_Measure(&humi_val, &checksum, HUMI);
if(err != 0)
{
SHT10_ConReset();
}
else
{
SHT10_Calculate(temp_val, humi_val, &temp_val_real, &humi_val_real);
// dew_point = SHT10_CalcuDewPoint(temp_val_real, humi_val_real);
sprintf( (void *)tempBuf,"%2.1fC %2.1f%%",temp_val_real, humi_val_real);
LcdPutString16_8(0,0,(uint8_t*)tempBuf,12,1);
}
Delay(1);
}
}
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_DMA_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
EnableUartIT();
printf("LPMD007-LPSENSOR-SDK-V1.00\r\n");
BoardInitPeriph();
/* Infinite loop */
while (1)
{
}
}
void BeepPortempInit(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = BEEP1_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(BEEP1_PORT, &GPIO_InitStruct);
GpioWrite(BEEP1_PORT,BEEP1_PIN,GPIO_PIN_SET);
GPIO_InitStruct.Pin = TC77_DIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(TC77_DIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = TC77_CLK_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(TC77_CLK_PORT, &GPIO_InitStruct);
GpioWrite(TC77_CLK_PORT,TC77_CLK_PIN,GPIO_PIN_RESET);
GPIO_InitStruct.Pin = TC77_NSS_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(TC77_NSS_PORT, &GPIO_InitStruct);
GpioWrite(TC77_NSS_PORT,TC77_NSS_PIN,GPIO_PIN_RESET);
}
void BeepPortemp_main(void)
{
int8 temperature;
int16 light;
char tempBuf[12];
uint8_t dispcount=0;
BeepPortempInit();
while(1)
{
KeyScan();
DelayMs(10);
if(KeyValue==HAL_KEY_CENTER)
{
GpioToggle( BEEP1_PORT,BEEP1_PIN);
}
NB-IOT 实验指导手册
119 / 214
//采集温度
temperature = ReadTc77Temp();
//采集光敏电阻
light = AdcMcuReadChannel( &hadc1,ADC_CHANNEL_0 );
if(dispcount++>100)
{
dispcount=0;
sprintf( (void *)tempBuf,"T:%2d L:%4d ",temperature,light);
LcdPutString16_8(0,0,(uint8_t*)tempBuf,12,1);//CENTER
}
}
}
void Realy2Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = RELAY1_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(RELAY1_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = RELAY2_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(RELAY2_PORT, &GPIO_InitStruct);
}
uint8_t KeyValue = HAL_KEY_NULL;
void KeyScan(void)
{
static uint8_t g_ucLastKeypad, KeyNum, ShortKey, LongKey;
uint8_t KeyStatus = 0;
KeyStatus = KeyRead();
ShortKey = ShortKey;
LongKey = LongKey;
if(g_ucLastKeypad == HAL_KEY_NULL && KeyStatus != g_ucLastKeypad){//按键刚被按下
KeyNum = 0;
ShortKey = 0;
LongKey = 0;
KeyValue = HAL_KEY_NULL;
}
else if(KeyStatus == g_ucLastKeypad && KeyStatus != HAL_KEY_NULL){//按键正被按着
KeyNum++;
if(KeyNum == KEY_LONG_TIMES){
ShortKey = 0;
LongKey = 1;
KeyValue = g_ucLastKeypad;
}
}
else if(KeyStatus == HAL_KEY_NULL && KeyStatus != g_ucLastKeypad){//按键被放开
if(KeyNum < KEY_LONG_TIMES && KeyNum > KEY_SHORT_TIMES){
KeyNum = 0;
ShortKey = 1;
LongKey = 0;
KeyValue= g_ucLastKeypad;
}
}
else{
KeyNum = 0;
ShortKey = 0;
LongKey = 0;
KeyValue = HAL_KEY_NULL;
}
g_ucLastKeypad = KeyStatus;
}
void Realy2_main(void)
{
Realy2Init();
LcdPutString16_8(0,0," K1&K2-CLOSE",12,1);//CENTER
while(1)
{
KeyScan();
DelayMs(10);
switch (KeyValue)
{
case HAL_KEY_UP:
GpioWrite(RELAY1_PORT,RELAY1_PIN,GPIO_PIN_RESET);
LcdPutString16_8(0,0," K1-CLOSE ",12,1);//UP
break;
case HAL_KEY_DOWN:
GpioWrite(RELAY1_PORT,RELAY1_PIN,GPIO_PIN_SET);
LcdPutString16_8(0,0," K1-OPEN ",12,1);//DOWN
break;
case HAL_KEY_LEFT:
GpioWrite(RELAY2_PORT,RELAY2_PIN,GPIO_PIN_RESET);
LcdPutString16_8(0,0," K2-CLOSE ",12,1);//LEFT
break;
case HAL_KEY_RIGHT:
GpioWrite(RELAY2_PORT,RELAY2_PIN,GPIO_PIN_SET);
LcdPutString16_8(0,0," K2-OPEN ",12,1);//RIGHT
break;
case HAL_KEY_CENTER:
GpioWrite(RELAY1_PORT,RELAY1_PIN,GPIO_PIN_RESET);
GpioWrite(RELAY2_PORT,RELAY2_PIN,GPIO_PIN_RESET);
LcdPutString16_8(0,0," K1&K2-CLOSE",12,1);//CENTER
break;
default:
break;
}
}
}
实验目的:了解并掌握可燃气体传感器的采集原理。
实验效果:通过 OLED 液晶屏显示当前传感器的检测值。
硬件介绍:
这里选用 MQ-5 气体传感器。MQ-5 气体传感器所使用的气敏材料是在清洁空气中电导率
较低的二氧化锡(SnO2)。当传感器所处环境中存在可燃气体时,传感器的电导率随空气中
可燃气体浓度的增加而增大。使用简单的电路即可将电导率的变化转换为与该气体浓度相
对应的输出信号。
MQ-5 气体传感器对丁烷、丙烷、甲烷灵敏度高,对甲烷和丙烷可较好的兼顾,这种传感
器可检测多种可燃性气体,特别是液化气(丙烷),是一款适合多种应用的低成本传感器。
P14 为电子开关控制引脚,当 P14 输入高电平三极管 Q1 导通,气体传感器 5 脚与 GND 导
通,传感器开始工作。P14 与 MCU 的 PB1 连通。
根据可燃气体传感器的电路原理图可知,可燃气体传感器的探测引脚为 P00,当可燃气体
传感器检测到可燃气时,P00 引脚电压会产生变化,探测引脚 P00 与 MCU 的 PA7 引脚
相连,所以在此实验中,我们采集 PA4 引脚的电压就可判断出可燃气体浓度水平。
电路原理图:
实现代码:
void GasInit(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GASPW_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GASPW_PORT, &GPIO_InitStruct);
GpioWrite(GASPW_PORT,GASPW_PIN,GPIO_PIN_RESET);
}
void Gas_main(void)
{
int16 gas;
char tempBuf[12];
uint8_t dispcount=0;
GasInit();
GpioWrite(GASPW_PORT,GASPW_PIN,GPIO_PIN_SET);
while(1)
{
DelayMs(10);
//采集可燃气体传感器
gas = AdcMcuReadChannel( &hadc1,ADC_CHANNEL_7 );
if(dispcount++>100)
{
dispcount=0;
sprintf( (void *)tempBuf,"gas:%04d ",gas);
LcdPutString16_8(0,0,(uint8_t*)tempBuf,12,1);//CENTER
}
}
}
实验目的:了解并掌握高亮 LED 的调节亮度原理。
实验效果:通过按键控制 MCU 输出不同占空比的 PWM 波形,并驱动点亮不同亮度 LED。
PWM脉冲宽度调制介绍:
脉冲宽度调制是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。我们可以让定时器产生 PWM, 在计数器频率固定时,PWM 频率或者周期由自动重载寄存器(TIMx_ARR)的值决定,其占空比由捕获/比较寄存器(TIMx_CCRx)的值决定。
实现代码:
void LedlightInit(void)
{
/* Compute the prescaler value to have TIM9 counter clock equal to 16000000 Hz */
uhPrescalerValue = (uint32_t)(SystemCoreClock / 16000000) - 1;
/* Initialize TIMx peripheral as follows:
+ Prescaler = (SystemCoreClock / 16000000) - 1
+ Period = (666 - 1)
+ ClockDivision = 0
+ Counter direction = Up
*/
TimHandle.Instance = TIMxx;
TimHandle.Init.Prescaler = uhPrescalerValue;
TimHandle.Init.Period = PERIOD_VALUE;
TimHandle.Init.ClockDivision = 0;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
}
/*##-2- Configure the PWM channels #########################################*/
/* Common configuration for all channels */
sConfig.OCMode = TIM_OCMODE_PWM1;
sConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfig.OCFastMode = TIM_OCFAST_DISABLE;
sConfig.OCIdleState = TIM_OCIDLESTATE_RESET;
/* Set the pulse value for channel 1 */
sConfig.Pulse = 0;//PULSE1_VALUE;
if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_1) != HAL_OK)
{
/* Configuration Error */
Error_Handler();
}
/*##-3- Start PWM signals generation #######################################*/
/* Start channel 1 */
if (HAL_TIM_PWM_Start(&TimHandle, TIM_CHANNEL_1) != HAL_OK)
{
/* PWM generation Error */
Error_Handler();
}
}
void Ledlight_main(void)
{
char tempBuf[12];
LedlightInit();
LcdPutString16_8(0,0," LEV0 ",12,1);//CENTER
while(1)
{
KeyScan();
DelayMs(10);
if((KeyValue==HAL_KEY_UP)||(KeyValue==HAL_KEY_RIGHT))
{
if(level<4)
level+=1;
else
level=4;
}
else if((KeyValue==HAL_KEY_DOWN)||(KeyValue==HAL_KEY_LEFT))
{
if(level>0)
level-=1;
else
level=0;
}
if(KeyValue!=HAL_KEY_NULL)
{
sprintf( (void *)tempBuf," LEV%d ",level);
LcdPutString16_8(0,0,(uint8_t*)tempBuf,12,1);//CENTER
HAL_TIM_PWM_Stop(&TimHandle, TIM_CHANNEL_1);
sConfig.Pulse = (uint32_t)pulsevalue_t[level];
HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&TimHandle, TIM_CHANNEL_1);
}
}
}
实验效果:
复位 NB-IOT 核心模块后,正确识别传感器模块,OLED 模块显示传感器类型" LED-Light"。
通过向上向下拨动方向按键,MCU 输出不同占空比的 PWM 波形,并驱动点亮不同亮度
LED。