基于STM32的物联网监控小车

本文介绍了一款基于STM32F103C8T6的物联网监控小车设计,包括STM32最小系统、红外传感器避障、电机驱动、联网模块(ESP8266)以及电量显示。通过红外传感器实现避障,电机驱动控制小车运动,ESP8266实现与云端的通信,电量显示通过ADC监测电池状态。整个设计利用STM32的基本外设功能,如GPIO、EXTI、TIM、ADC、USART。

         通过 STM32与红外循迹模块对小车自动控制,将小车与联网模块、定位模块连接,小车提供联网模块与云端连接,实现信息的上传,同时若有需要,也可以通过云端下发命令控制小车行进。

主控:STM32F103C8T6最小系统版
传感器:红外传感器
电机驱动:TB6612
联网模块:ESP8266
GPS:SR2828Z1
编程:KEIL5(库函数)
云平台:腾讯云

目录

一、STM32F103C8T6最小系统版

二、红外传感器

三、电机驱动

 四、联网模块

五、电量显示

六、信息传递

七、结束


一、STM32F103C8T6最小系统版

        实物图如下

        STM32F103片上外设有下表

        而本设计需要用到的外设主要有NVIC、RCC、GPIO、EXTI、TIM、ADC、USART,这些外设都是STM32最基本的,也是必须掌握的。

        本设计主要使用得STM32功能主要为要想外设与STM32能正常地联系工作,就要清楚STM32引脚的主要功能。结合上表与实物图选择相应的引脚连接。

二、红外传感器

        主要使用淘宝就能买到的红外传感器,用来实现避障的简单功能。实物图如下。

         这个传感器工作原理简单啊概括就是收到反射的信号就输出低电平。因此在程序中检测STM32连接传感器的引脚是否收到低电平就可以判断小车是否遇到障碍物。传感器上的十字旋钮是调整敏感度的。

        要实现传感器与stm32的配合就要使用GPIO外设功能。当然,对于判断引脚电平不停使用库函数既麻烦代码也不简洁。因此可以使用位带操作的方法来直接使用if(PNin(n))来进行逻辑判断。

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#ifndef __CH_LIB_GPIO_H__
#define __CH_LIB_GPIO_H__

#ifdef __cplusplus
 extern "C" {
#endif
	 
//位带操作,实现51类似的GPIO控制功能
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    

#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
#define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
#define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
#define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
#define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08 
 
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

/* GPIO端口定义 */
#define HW_GPIOA  (0x00U) 
#define HW_GPIOB  (0x01U)
#define HW_GPIOC  (0x02U)
#define HW_GPIOD  (0x03U)
#define HW_GPIOE  (0x04U)


#endif

         这样就可以直接使用PNin(n)来指代引脚的输入状态。

        下面进行GPIO配置。下面是直接包装好的配置函数,对于红外传感器一般使用上拉输入。

u8 GPIO_QuickInit(int instance, int GPIO_Pin_x, GPIOMode_TypeDef Mode)
{
	 /* config state */
	  GPIO_InitTypeDef  GPIO_InitStructure;
    switch(instance)
    {
        case HW_GPIOA:
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	
						GPIO_InitStructure.GPIO_Pin = GPIO_Pin_x;				
						GPIO_InitStructure.GPIO_Mode = Mode; 		
						GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
						GPIO_Init(GPIOA, &GPIO_InitStructure);					 //根据设定参数初始
            break;
				
        case HW_GPIOB:
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	
						GPIO_InitStructure.GPIO_Pin = GPIO_Pin_x;	
						if(GPIO_Pin_x == GPIO_Pin_4  || GPIO_Pin_x == GPIO_Pin_3 ){
							RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
							GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
						}				
						GPIO_InitStructure.GPIO_Mode = Mode; 		
						GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
						GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始
            break;
        case HW_GPIOC:
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);	
						GPIO_InitStructure.GPIO_Pin = GPIO_Pin_x;				
						GPIO_InitStructure.GPIO_Mode = Mode; 		 
						GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
						GPIO_Init(GPIOC, &GPIO_InitStructure);					 //根据设定参数初始
            break;
        case HW_GPIOD:
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);	
						GPIO_InitStructure.GPIO_Pin = GPIO_Pin_x;				
						GPIO_InitStructure.GPIO_Mode = Mode; 		
						GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
						GPIO_Init(GPIOD, &GPIO_InitStructure);					 //根据设定参数初始
            break;
        case HW_GPIOE:
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);	
						GPIO_InitStructure.GPIO_Pin = GPIO_Pin_x;				
						GPIO_InitStructure.GPIO_Mode = Mode; 		 
						GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
						GPIO_Init(GPIOE, &GPIO_InitStructure);					 //根据设定参数初始
            break;
        default:
						return 0;					
    }
		return  1;
}

        这样GPIO就基本配置完成,我们只要在主函数中检测引脚状态就可以自动判断是否存在障碍物。例如我把传感器接在PA4,当出现障碍物让小车急停。

while(PAin(4))
{
    carstop();
}

三、电机驱动

        既然是小车就要有轮子,有轮子就要用舵机让车动起来。如何顺利驱动四个轮子呢?就是用电机驱动的芯片实现。下面以TB6612为例。实物图、引脚电路图如下。

                              

        驱动表如下。

        有了传感器工作为基础,这就简单多了。无非又是用GPIO外设控制罢了,不过这次是输出而不是输入哦。通过GPIO函数配置推挽输出,再排列组合出小车的各种运动姿态。

/*宏定义右轮*/
#define WHEEL_A_PWM PBout(7)
#define WHEEL_A_1 PBout(8)//前进轮
#define WHEEL_A_2 PBout(9)
/*宏定义左轮*/
#define WHEEL_B_PWM PBout(6)
#define WHEEL_B_1 PBout(5)//前进轮
#define WHEEL_B_2 PBout(4)


void carforward(void)
{
	WHEEL_A_PWM = 1;
	WHEEL_A_1 = 0;
	WHEEL_A_2 = 1;
	
	WHEEL_B_PWM = 1;
	WHEEL_B_1 = 0;
	WHEEL_B_2 = 1;
}

void carstop(void)
{
	WHEEL_A_PWM = 0;
	WHEEL_A_1 = 0;
	WHEEL_A_2 = 0;
	
	WHEEL_B_1 = 0;
	WHEEL_B_2 = 0;
	WHEEL_B_PWM = 0;
}

void carback(void)
{
	WHEEL_A_PWM = 1;
	WHEEL_A_1 = 1;
	WHEEL_A_2 = 0;
	
	WHEEL_B_1 = 1;
	WHEEL_B_2 = 0;
	WHEEL_B_PWM = 1;
}

void turn_right(void)
{
	WHEEL_A_PWM = 0;
	WHEEL_A_1 = 0;
	WHEEL_A_2 = 0;
	
	WHEEL_B_1 = 0;
	WHEEL_B_2 = 1;
	WHEEL_B_PWM = 1;
}	

void turn_left(void)
{
	WHEEL_A_PWM = 1;
	WHEEL_A_1 = 0;
	WHEEL_A_2 = 1;
	
	WHEEL_B_PWM = 0;
	WHEEL_B_1 = 0;
	WHEEL_B_2 = 0;
}

void turn_right_in_place(void)
{
	WHEEL_A_PWM = 1;
	WHEEL_A_1 = 1;
	WHEEL_A_2 = 0;
	
	WHEEL_B_PWM = 1;
	WHEEL_B_1 = 0;
	WHEEL_B_2 = 1;
}

void turn_left_in_place(void)
{
	WHEEL_A_PWM = 1;
	WHEEL_A_1 = 0;
	WHEEL_A_2 = 1;
	
	WHEEL_B_PWM = 1;
	WHEEL_B_1 = 1;
	WHEEL_B_2 = 0;
}

         当然,如果觉得车速过高or低,可以通过PWM波的方法来控制车速,这里就不赘述了。

 四、联网模块

        既然标题有物联网,就少不了使用ESP8266。实物图、引脚电路图如下。

                    

         在使用ESP8266上云方面我选择讨巧使用AT固件的方法,使用AT固件固然简单,但对代码移植和个人学习都不利,但是我就不是物联网专业的,所以选择所以讨巧的方法。

        具体方式我写过文章。

STM32+ESP8266使用MQTTAT固件连接云_esp8266 mqtt固件_英趣斯挺的博客-优快云博客

五、电量显示

        小车电量是很重要的参数,通过对小车电量的多少可以知道小车是否能够运行。本设计通过使用STM32的模数转换器来计算小车的电量多少。通过串联电阻对电源进行分压处理,电容进行滤波,使得模数转换器测量的电压在3.3V以下。图AD采样电路图。

        由于本设计只有电量需要使用模数转换器,在程序配置中采用单通道单次扫描的方式进行转换。通过判断转换标志位(ADC_FLAG_EOC)来判断转换是否完成,当转换完成后使用函数读取数据寄存器的到转换后的值。考虑到数据的波动影响,对数据进行均值滤波。

#include "stm32f10x.h"                  // Device header

u16 ADValue;
float Power;

void AD_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;             
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		        
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
	
	ADC_InitTypeDef ADC_InitSturcture;
	ADC_InitSturcture.ADC_Mode = ADC_Mode_Independent;
	ADC_InitSturcture.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitSturcture.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitSturcture.ADC_ContinuousConvMode = DISABLE;
	ADC_InitSturcture.ADC_ScanConvMode = DISABLE;	
	ADC_InitSturcture.ADC_NbrOfChannel = 1;

	ADC_Init(ADC1,&ADC_InitSturcture);
	
	ADC_Cmd(ADC1,ENABLE);

//开始校准
	ADC_ResetCalibration(ADC1);
	while (ADC_GetResetCalibrationStatus(ADC1));
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1));
}

u16 AD_GetValue(void)
{
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	while (ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);
	return ADC_GetConversionValue(ADC1);
}

u16 CarPower(void)
{
	u32 temp = 0;
	u8 t;
	u32 AD_Value;
	double AD_Final_Value;
	double Power;
	
	for(t=0;t<10;t++)
	{
		temp += AD_GetValue();
	}
	AD_Value = temp / 10;
	AD_Final_Value = 3 * (float)AD_Value / 4095 * 3.3;
	if(AD_Final_Value > 9.9) {AD_Final_Value = 9.9 ;}
	Power = AD_Final_Value * 10;
	return(Power);
}

         AD转换器功能当然不止转换电量那么简单。可以考虑在小车放置光敏传感器、热敏传感器、湿度传感器等,通过AD转换器得到数值信息。转换多个模拟信号就要采用多通道的方法,同时考虑到数据的实时性,要采用DMA的方法转运数据。这里也不过多赘述。

六、信息传递

        联网模块和GPS模块都是需要与stm32实现串口的信息传递的,所以,这就需要使用stm32的USART外设。同时,因为信息传递要有实时性,我们一旦接受到信息就要立刻解包,使用这里要使用到接受中断NVIC。将两个模块连接到串口1,2,通过串口接收中断,在中断程序中解包,把信息传递给stm32。下面是包装好的串口配置函数。

/**
 * @brief  串口快速初始化程序
 * @code
 *      初始化UART2: 9600-0-8-1,主优先级preepri=2,响应优先级subpri=2,使能中断ITsta=ENABLE
 *      UART_QuickInit(HW_UART2, 9600, 2, 2, ENABLE);
 * @endcode
 * @param[in]   instance 模块号
 *              @arg HW_UART1 1端口
 *              @arg HW_UART2 2端口
 *              @arg HW_UART3 3端口
 * @param[in]   bound   波特率: 9600,115200....
 * @param[in]   preepri 抢占优先级,默认group2,可选值0,1,2,3
 * @param[in]   subpri  响应优先级,默认group2,可选值0,1,2,3
 * @param[in]   ITsta   使能/失能中断,可选值ENABLE,DISABLE
 * @retval      UART初始化结果,1初始化正确,0初始化失败
 */
uint8_t UART_Config(int instance, int bound, int preepri, int subpri,  FunctionalState ITsta)
{
		GPIO_InitTypeDef GPIO_InitStructure;
	  USART_InitTypeDef USART_InitStructure;
	  NVIC_InitTypeDef NVIC_InitStructure;
		
		switch(instance)
    {
        case HW_UART1:
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
						USART_DeInit(USART1);                                   //复位串口1
							
						GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;               //USART1_TX   PA.9
						GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
						GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	        //复用推挽输出
						GPIO_Init(GPIOA, &GPIO_InitStructure);                  //初始化PA9  
							
						GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;              //USART1_RX	  PA.10
						GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   //浮空输入
						GPIO_Init(GPIOA, &GPIO_InitStructure);                  //初始化PA10

						NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
						NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=preepri ;//抢占优先级3
						NVIC_InitStructure.NVIC_IRQChannelSubPriority = subpri;		    //子优先级3
						NVIC_InitStructure.NVIC_IRQChannelCmd = ITsta;			          //IRQ通道使能
						NVIC_Init(&NVIC_InitStructure);	                              //根据指定的参数初始化VIC寄存器						

						USART_InitStructure.USART_BaudRate = bound;                		//一般设置为9600;
						USART_InitStructure.USART_WordLength = USART_WordLength_8b;   //字长为8位数据格式
						USART_InitStructure.USART_StopBits = USART_StopBits_1;        //一个停止位
						USART_InitStructure.USART_Parity = USART_Parity_No;           //无奇偶校验位
						USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
						USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

						USART_Init(USART1, &USART_InitStructure);                     //初始化串口
						USART_ITConfig(USART1, USART_IT_RXNE, ITsta);                 //开启中断
						USART_Cmd(USART1, ENABLE);                                    //使能串口 				
				
        case HW_UART2:
						RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);	//使能USART1,GPIOA时钟
						USART_DeInit(USART2);                                   //复位串口1
							
						GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;               //USART1_TX   PA.9
						GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
						GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	        //复用推挽输出
						GPIO_Init(GPIOA, &GPIO_InitStructure);                  //初始化PA9  
							
						GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;              //USART1_RX	  PA.10
						GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   //浮空输入
						GPIO_Init(GPIOA, &GPIO_InitStructure);                  //初始化PA10

						NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
						NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=preepri ;//抢占优先级3
						NVIC_InitStructure.NVIC_IRQChannelSubPriority = subpri;		  //子优先级3
						NVIC_InitStructure.NVIC_IRQChannelCmd = ITsta;			    //IRQ通道使能
						NVIC_Init(&NVIC_InitStructure);	                        //根据指定的参数初始化VIC寄存器						

						USART_InitStructure.USART_BaudRate = bound;              //一般设置为9600;
						USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
						USART_InitStructure.USART_StopBits = USART_StopBits_1;     //一个停止位
						USART_InitStructure.USART_Parity = USART_Parity_No;        //无奇偶校验位
						USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
						USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

						USART_Init(USART2, &USART_InitStructure);                     //初始化串口
						USART_ITConfig(USART2, USART_IT_RXNE, ITsta);                //开启中断
						USART_Cmd(USART2, ENABLE);                    //使能串口 				 //根据设定参数初始
            break;
        case HW_UART3:
						RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB, ENABLE);	//使能USART1,GPIOA时钟
						USART_DeInit(USART3);                                   //复位串口1
							
						GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;               //USART1_TX   PA.9
						GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
						GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	        //复用推挽输出
						GPIO_Init(GPIOB, &GPIO_InitStructure);                  //初始化PA9  
							
						GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;              //USART1_RX	  PA.10
						GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   //浮空输入
						GPIO_Init(GPIOB, &GPIO_InitStructure);                  //初始化PA10

						NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
						NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=preepri ;//抢占优先级3
						NVIC_InitStructure.NVIC_IRQChannelSubPriority = subpri;		  //子优先级3
						NVIC_InitStructure.NVIC_IRQChannelCmd = ITsta;			    //IRQ通道使能
						NVIC_Init(&NVIC_InitStructure);	                        //根据指定的参数初始化VIC寄存器						

						USART_InitStructure.USART_BaudRate = bound;              //一般设置为9600;
						USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
						USART_InitStructure.USART_StopBits = USART_StopBits_1;     //一个停止位
						USART_InitStructure.USART_Parity = USART_Parity_No;        //无奇偶校验位
						USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
						USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

						USART_Init(USART3, &USART_InitStructure);                     //初始化串口
						USART_ITConfig(USART1, USART_IT_RXNE, ITsta);                //开启中断
						USART_Cmd(USART3, ENABLE);                    //使能串口 				 //根据设定参数初始
            break;
        
        default:
						return 0;					
    }
		return 1;
}

七、结束

        这篇文章其实都是相当基础的东西,想要驱动小车,就要实现stm32上很基础的外设功能。这也是我第一次写这么长的文章,如有纰漏,敬请指正。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值