STM32-USART串口通讯+MG90s舵机+ESP01s

前言

学习stm32需要记忆的东西太多了,网上很多博客都有写如何做舵机控制,也有写如何通过串口与上位机通信,这篇博客主要通过TCP客户端发送指令控制舵机运动到对应的角度,所以这篇博客的主要目的是记录下自己写下整个控制的流程,便于自己温故。

硬件

使用的板子是 STM32F103 C8T6 ,最小系统板,好兄弟们要借鉴我的话如果架构不一样可以参照 “STM32官方手册” 进行小修改。
舵机型号是 MG90s 淘宝上买10块钱买来的,是一个控制较为简单的角度伺服控制器,通过输出PWM波形控制旋转角度。
以及若干杜邦线,由于学习板上有很多外设,其他就直接用板子上的。

小结
开发板:STM32F103 C8T6最小系统板
舵机: MG90s

整体流程如下

ESP01s STM32 MG90s LED 发送信号 FUNC,PARAM, USART通信 控制角度 TIM输出PWM波 发送回执信息 控制亮暗 GPIO口输出 发送回执信息 ESP01s STM32 MG90s LED

接线

舵机部分

如图所示,使用的是PB9作为TIM4的CH4口复用,也可选用其他端口如:PA1作为TIM5的CH2口复用。需要使用TIM输出PWM波,详情可见博客:

舵机参考博客1
舵机参考博客2

请添加图片描述
MG90s的黄色、红色和褐色分别对应信号线、VCC和VDD,因此这里我们把黄色接入PB9,红色接入5v,褐色接入GND。

串口部分

如图所示,STM手册上有使用的是PA2作为USART2的TX口复用PA3作为USART2的RX口复用,串口通信详情见如下博客。

串口USART参考博客1

请添加图片描述

LED部分

由于板子上有一个自带的LED小灯,其挂在PC13上因此无需接线。

小结

最后接线如图
请添加图片描述

代码

在本示例中使用的外设有GPIO、TIM、USART、RCC、EXTI、FSMC,在Keil中选择外设勾选上。

舵机代码

  • mg90s.h
#ifndef _TIM_H_
#define _TIM_H_
#include "stm32f10x.h"

extern u8 ANGLE;

void TIM_init(void);

void TIM_Angle(float angle);

#endif


  • mg90s.c
#include "mg90s.h"

/*
	使用PWM波形输出步骤
	1、定时器时钟使能
	2、初始化定时器
		a.预分频系数
		b.重装载值
		c.计数模式
	3、比较输出口设置
		a.端口配置
		b.端口重映射
		c.TIM比较配置
	4、使能定时器
*/

#define ARR 9999		//重装载值	->设置为10000-1 比较容易计算占空比
#define PSC 143			//预分频系数

u8 ANGLE=0;
//	@desc:	MG90s初始化
//	@param:	psc:预分频系数	arr:重装载值
//	@rtn:	None
void TIM_init(void){
	
	TIM_TimeBaseInitTypeDef T4;
	TIM_OCInitTypeDef OC4;
	GPIO_InitTypeDef PB9;
	
	/*时钟使能*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
	
	/*GPIOB-PB9初始化*/
	PB9.GPIO_Mode = GPIO_Mode_AF_PP;
	PB9.GPIO_Pin = GPIO_Pin_9;
	PB9.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &PB9);
	
	/*TIM4 初始化*/
	T4.TIM_Prescaler = PSC;
	T4.TIM_Period = ARR;
	T4.TIM_CounterMode = TIM_CounterMode_Up;
	T4.TIM_ClockDivision = TIM_CKD_DIV1;
	T4.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM4, &T4);
	
	/*OC4初始化*/
	OC4.TIM_OCMode = TIM_OCMode_PWM1;
	OC4.TIM_OCNPolarity = TIM_OCNPolarity_High;
	OC4.TIM_OutputState = TIM_OutputState_Enable;
	OC4.TIM_Pulse = 0;
	TIM_OC4Init(TIM4, &OC4);
	
	TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);
	TIM_ARRPreloadConfig(TIM4, ENABLE);
	TIM_Cmd(TIM4, ENABLE);
}

//	@desc:	MG90s设置角度,内部有将角度转换为比较值,在修改重装载值时需要修改
//	@param:	angle:预期角度值
//	@rtn:	None
void TIM_Angle(float angle){
	angle = (u16)(50.0*angle/9.0+249.0);
	TIM_SetCompare4(TIM4, angle);
}

串口代码

  • usart.h
#ifndef _USART_H_
#define _USART_H_

#include "stm32f10x.h"

#define IS_SWITCH(PARAM) ((PARAM == 0)  || (PARAM == 1) )
#define IS_ANGLE(PARAM)  ((PARAM <= 180)|| (PARAM >= 0) )

extern u8 USART_BUF[80];		//数据缓冲区

void USART_init(void);

void USART_SendString(USART_TypeDef *USARTx, u8* String);

void Decode_String(u8* String);
void LED_USART_turn(u8 Switch);
void SVG_USART_turn(u8 Angle);
void FUN_USART_turn(u8);

#endif

  • usart.c
#include "usart.h"
#include "led.h"
#include "tim.h"

//	@desc:	USART初始化
//	@param:	None
//	@rtn:	None
void USART_init(void){
	
	GPIO_InitTypeDef GPA;
	USART_InitTypeDef U2;
	NVIC_InitTypeDef N2;
	/*时钟使能*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	/*TX端口设置*/
	GPA.GPIO_Mode = GPIO_Mode_AF_PP;
	GPA.GPIO_Pin = GPIO_Pin_2;
	GPA.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPA);//TX
	/*RX端口设置*/
	GPA.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPA.GPIO_Pin = GPIO_Pin_3;
	GPIO_Init(GPIOA, &GPA);//RX
	/*USART端口设置*/
	USART_DeInit(USART2);
	U2.USART_BaudRate = 115200;
	U2.USART_WordLength = USART_WordLength_8b;
	U2.USART_StopBits = USART_StopBits_1;
	U2.USART_Parity = USART_Parity_No;
	U2.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;
	U2.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_Init(USART2, &U2);
	/*中断服务配置*/
	N2.NVIC_IRQChannel = USART2_IRQn;
	N2.NVIC_IRQChannelCmd = ENABLE;
	N2.NVIC_IRQChannelPreemptionPriority = 2;
	N2.NVIC_IRQChannelSubPriority = 3;
	NVIC_Init(&N2);
	/*设置中断触发*/
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
	/*串口使能*/
	USART_Cmd(USART2, ENABLE);
	
}

u8 USART_BUF[80];		//数据缓冲区
u16 USART_INDEX = 0;	//缓冲区索引
u8 LED_TEXT[] = "LED SETTING\n";
u8 SGV_TEXT[] = "SGV SETTING\n";
u8 WRG_TEXT[] = "WRONG PARAMETER\n";
u8 AP_CONNECT[] = "AT+CIPSEND=0\n";

u8 AP_sw = 0;
/*串口2服务函数*/
//	@param:	None
//	@rtn:	None
void USART2_IRQHandler(void){
	
	if(USART_GetITStatus(USART2, USART_IT_RXNE)){			//终端类型判断
		
		USART_ClearITPendingBit(USART2, USART_IT_RXNE);		//清除中断标志
		USART_BUF[USART_INDEX] = USART_ReceiveData(USART2);	//数据存入缓冲区
		USART_INDEX++;
		
		if(USART_BUF[USART_INDEX-1] == 0x0a || USART_INDEX == 80){	//若最后接收到0x0a或长度上限
			
			//判断1
			if(USART_BUF[0] == '+'){
				/*+ 为命令头,用以标识由串口调试*/
				USART_SendString(USART2, USART_BUF+1);
				Decode_String(USART_BUF+1);
				
			//判断2
			}else if(USART_BUF[0] <= '4' && USART_BUF[0] >= '0'){
				/*检测到有设备连接*/
				AP_CONNECT[11] = USART_BUF[0];
				USART_SendString(USART2,AP_CONNECT);
				
			//判断3
			}else{
				/*其他情况将静默XXX所接受的数据发送*/
//				USART_SendString(USART2, USART_BUF);
				USART_INDEX = 0;	//索引清零
			}
				USART_INDEX = 0;	//索引清零
		}
	}
}


/*串口发送字符串*/
//	@param:	USARTx:发送端口	String:发送字符
//	@rtn:	None
void USART_SendString(USART_TypeDef *USARTx, u8* String){
	
	u8 *pt = String;
	
	while(*pt != 0x0a || pt-String >= 80){
		//判断结尾
		USART_SendData(USARTx, *pt);								//发送
		
		while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == 0){};	//等待发送完毕
		pt++;
	};
	USART_SendData(USARTx, 0x0d);
	while(USART_GetFlagStatus(USARTx, USART_FLAG_TC)  == 0){};
	USART_SendData(USARTx, 0x0a);									//等待发送0x0a结束
}

/*解码字符串*/
//	@param:	String:需解码字符
//	@rtn:	None
void Decode_String(u8* String){
	u8 idx = 0;				//定义索引
    u8 value = 0;			//定义传送参数
	void (*fun)(u8);		//定义函数指针
	u8* pt;
	while(*String != ':'){	//判断字符头,":"后的才是发送的有效数据,如  +ip,0,12:youxiaozifu
		pt = String+2;
        String++;
	}
	//判断控制LED
	if( ( *(pt)   == 'L' || *(pt)   == 'l' ) && 
		( *(pt+1) == 'E' || *(pt+1) == 'e' ) &&
		( *(pt+2) == 'D' || *(pt+2) == 'd' )	){
			
		fun = LED_USART_turn;	//指向LED
			
	//判断控制舵机
	}else if(
		( *(pt)   == 'S' || *(pt)   == 's' ) && 
		( *(pt+1) == 'V' || *(pt+1) == 'v' ) &&
		( *(pt+2) == 'G' || *(pt+2) == 'g' )	){
			
		fun = SVG_USART_turn;	//指向舵机
			
	//其他功能
	}else{
		
        fun = FUN_USART_turn;	//指向其他
		
    }
	//获取 传送数值
	for(idx = 4; *(pt+idx)!= ','; idx++) {
        value *= 10;
        value += *(pt+idx) - '0';
    }
	//作用于控制的功能上
    fun(value);
}


/*LED功能*/
void LED_USART_turn(u8 S){
	if(IS_SWITCH(S)){
		GPIO_WriteBit(GPIOC, GPIO_Pin_13, S);
		USART_SendString(USART2, LED_TEXT);
	}else{
		USART_SendString(USART2, WRG_TEXT);
	}
}

/*舵机功能*/
void SVG_USART_turn(u8 Angle){
//	if(IS_ANGLE(Angle)){
		ANGLE = Angle;
		USART_SendString(USART2, SGV_TEXT);
//	}else{
//		USART_SendString(USART2, WRG_TEXT);
//	}
}

/*待开发*/
void FUN_USART_turn(u8 param){
	USART_SendString(USART2, "WAITINGFORDEVELOPMENT\n");
}

LED代码

  • led.h
#ifndef _LED_H_
#define _LED_H_
#include "stm32f10x.h"
void LED_init(void);
void LED_turn(void);

#endif

*led.c

#include "stm32f10x.h"
#include "led.h"


void LED_init(void){
	GPIO_InitTypeDef GPIO_InitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin 	= GPIO_Pin_13;
	GPIO_InitStruct.GPIO_Speed  = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStruct);
	
	GPIO_ResetBits(GPIOC, GPIO_Pin_13);
}

void LED_turn(void){
	if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13))
		GPIO_ResetBits(GPIOC, GPIO_Pin_13);
	else
		GPIO_SetBits(GPIOC, GPIO_Pin_13);
}

MAIN函数

  • main.c
#include "stm32f10x.h"
#include "delay.h"
#include "led.h"
#include "usart.h"
#include "tim.h"


void setup(void);
void ESP_setup(void);


int main(){
	u16 i = 0, j;
	setup();		//硬件初始化
	LED_turn();
	ESP_setup();	//ESP初始化
	LED_turn();
	while(1){
		i++;
//		LED_turn();
//		delay_ms(2000);
//		LED_turn();
		delay_ms(50);
		TIM_Angle((float)ANGLE);
		if (i>=12000){
		
			for(j=0;j<70;j++)
				USART_BUF[j] = 0x0a;
			i = 0;
		}
	}
}

void setup(void){
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	delay_init();
	LED_init();
	USART_init();
	TIM_init();
}


u8 COMMAND[5][20] = {"AT+CWMODE=2\n", "AT+RST\n", "AT+CIPMUX=1\n", "AT+CIPSERVER=1,505\n", "AT+CIPSTO=300\n"};
void ESP_setup(void){
	u8 i = 0;
	for(;i<5;i++){
		USART_SendString(USART2, COMMAND[i]);
		delay_ms(1000);
	}
}

效果

晚上有空回宿舍录制视频,我在代码中的指令是FUNC,PARAM,的形式控制的,FUNC为功能,PARAM为调节参数。即控制led小灯开为LED,0,,关为LED,1,,舵机旋转134度为SVG,134,

  • 8
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小聪民

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值