文章目录
概要
本项目采用模块化编程
一、USART1 与 USART3 的运用
USART1 和 USART3 主要负责数据的传输。我们可以通过它们与其他设备进行通信,实现小狗与外界的互动(STM32 的 USART1 和 USART3 通常会有默认的引脚分配)。见下图
二、PWM 的功能实现
利用 PWM 技术,我们可以精确控制小狗的动作,比如通过调整占空比来模拟小狗的行走效果。
三、TIM 的应用
PWM(脉宽调制)的实现通常需要使用定时器(TIM)来提供精确的时间基准。定时器可以产生周期性的时钟信号,通过对定时器的配置,可以控制 PWM 信号的周期和占空比。利用定时器的计数功能,我们可以在特定的时间点切换输出电平,从而实现不同占空比的 PWM 信号。
四、I2C 控制 OLED
I2C 是连接 OLED 显示屏的重要方式。通过 I2C 协议,我们能够将小狗的图像和动画数据准确地传输到显示屏上,使其生动呈现。
整体架构流程
通过串口对蓝牙和语音识别功能进行接收和发送,从而实现对单片机的控制。通过 PWM 实现对舵机的控制。通过II2通信对 OLED 屏进行控制。
技术名词解释
PWM:脉冲宽度调制,通过调节脉冲的宽度来控制信号的平均值。
TIM:定时器,用于产生定时信号或进行时间测量。
USART:通用同步/异步收发器,用于串行数据通信。“TX”的全称是“Transmit”(发送),“RX”的全称是“Receive”(接收)。
II2:这个不太明确具体含义,可能是某种特定的标识或参数。
使能:意思是使某一功能或操作得以启用、生效或实现。
它通常用于描述开启或激活某个特定的能力或机制。
遍历:按照一定的规则依次访问和处理数据结构中的每个元素的过程。
它就像是逐一检查数据结构中的每一项,确保不遗漏任何一个元素。
常见的数据结构遍历包括数组遍历、链表遍历、树结构遍历等。通过遍历,可以对数据进行各种操作,如读取、修改、统计等。
技术细节
蓝牙部分
在定义 TX(发送)引脚时,通常初始化为推挽输出模式;而定义 RX(接收)引脚时,一般初始化为浮空输入或上拉输入模式。设置PA9,PA10分别为TX,RX.
#include "stm32f10x.h" // Device header
#include <stdio.h>
#include <stdarg.h>
#include "Delay.h"
#include <stdlib.h>
uint8_t USART1_RxData; //存储从串口接收到的数据的变量
uint8_t USART1_RxFlag; //指示串口接收状态的变量,通常用于表示是否有新的数据接收到、接收是否完成等状态信息。
void USART1_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启时钟
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出,TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//表示输入上拉模式。
在这种模式下,对应引脚被配置为输入模式,并且内部有上拉电阻,使得引脚在没有外部输入时保持高电平。
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600; //波特率
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//这里是选择不用硬件流控制,硬件流控制是一种数据传输的控制方式。它通过使用特定的硬件信号线(如 RTS/CTS 等)来在发送方和接收方之间进行通信,以协调数据的发送和接收速度,确保数据传输的准确性和稳定性,避免数据丢失或错乱。以下情况通常会使用硬件流控制:
高速数据传输:当数据传输速率较高,需要更精确地控制数据发送和接收时。
对数据准确性要求高:在一些关键应用中,为了保证数据的完整性,会采用硬件流控制。
长距离传输:在信号容易受到干扰的长距离传输中,硬件流控制有助于提高传输的可靠性。
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //两种模式均选择
USART_InitStructure.USART_Parity = USART_Parity_No; // Parity 的意思是奇偶,这里是选择不需要奇偶校验,奇偶校验是一种简单的校验方法,用于检测数据在传输或存储过程中是否发生错误。
它通过在数据编码中添加一位校验位,使数据中“1”的个数为奇数(奇校验)或偶数(偶校验)。接收方通过检查校验位来判断数据是否正确。
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位1位
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据位8位,8b就是8bit。
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置 NVIC 优先级分组为 2。
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //使能中断、抢占优先级和响应优先级都设置为 1。抢占优先级:指的是中断能够抢占其他低优先级中断的能力。具有较高抢占优先级的中断可以先被执行,从而中断正在执行的低优先级中断。
响应优先级:也称为子优先级,它决定了在具有相同抢占优先级的中断同时发生时的执行顺序。响应优先级较高的中断会先被处理。对比串口3,第一个串口的抢占优先级和响应优先级都为 1,意味着它在中断处理中的优先级相对较低。
第三个串口的抢占优先级为 1,与第一个串口相同,但响应优先级设置为 3,相对较高。这意味着在相同抢占优先级的中断中,第二个串口的中断会优先得到响应和处理
NVIC_Init(&NVIC_InitStructure);
USART_Cmd(USART1, ENABLE); //使能
}
//下面是一些函数
USART1_SendByte 和 USART1_SendArray 函数用于通过串口发送单个字节或数组数据。
USART1_SendString 函数用于发送字符串数据。
Serial_Pow 函数用于计算幂运算。
USART1_SendNumber 函数用于发送数字。
USART1_fputc 函数用于将字符输出到串口,使其可以像标准输出一样使用。
USART1_Printf 函数用于格式化并发送数据到串口。
USART1_GetRxFlag 和 USART1_GetRxData 函数用于获取接收标志和接收数据。
USART1_IRQHandler 函数是串口中断处理函数,用于处理接收数据的事件。
void USART1_SendByte(uint8_t Byte)
{
USART_SendData(USART1, Byte); //将指定的字节数据写入串口 1 的发送寄存器。
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //这是一个等待循环,它会一直等待,直到串口 1 的发送缓冲区为空(即发送完成标志 USART_FLAG_TXE 被设置)。
}
void USART1_SendArray(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for (i = 0; i < Length; i ++)
{
USART1_SendByte(Array[i]);
}
}//uint16_t i:定义一个循环变量 i。
for (i = 0; i < Length; i ++):通过循环,从数组的第一个元素开始,依次将每个元素作为一个字节发送出去。
在每次循环中,通过调用 USART1_SendByte(Array[i]),将当前数组元素发送出去。
void USART1_SendString(char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i ++)
{
USART1_SendByte(String[i]);
}
}
//uint8_t i:定义一个用于遍历字符串的索引变量i。
for (i = 0; String[i]!= '\0'; i ++):这是一个循环,从字符串的开头开始,一直到遇到字符串的结束标志('\0')为止。
在循环中,通过USART1_SendByte(String[i]),将字符串的每个字符依次作为一个字节发送出去。这样就实现了将整个字符串通过串口发送出去的功能。
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y --)
{
Result *= X;
}
return Result;
}
//Serial_Pow 函数用于计算一个数的指定次幂。
uint32_t Result = 1:初始化结果为 1。
while (Y --):这是一个循环,循环次数为 Y 次。
在循环中,通过不断将当前结果乘以 X,来计算幂值。每次循环后,Y 的值减 1。
当循环结束后,返回计算得到的幂值。
void USART1_SendNumber(uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i ++)
{
USART1_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
}
}
//uint8_t i:定义一个循环变量 i。
for (i = 0; i < Length; i ++):通过循环,从数字的高位到低位,逐位处理。
在每次循环中,计算当前位的数值,即 Number 除以 Serial_Pow(10, Length - i - 1) 取余后再加上 '0',将其转换为字符。然后使用 USART1_SendByte 发送该字符。这样就实现了将数字按位发送的效果。
int USART1_fputc(int ch, FILE *f)
{
USART1_SendByte(ch);
return ch;
}
//USART1_fputc 函数:
USART1_SendByte(ch):将输入的字符 ch 通过 USART1_SendByte 发送出去。
return ch:返回输入的字符。
void USART1_Printf(char *format, ...)
{
char String[100];
va_list arg;
va_start(arg, format);
vsprintf(String, format, arg);
va_end(arg);
USART1_SendString(String);
}//定义一个字符数组 String[100] 用于存储格式化后的字符串。
使用 va_list 和相关函数来处理可变参数,将格式化后的字符串存储到 String 中。
最后通过 USART1_SendString 将格式化后的字符串发送出去
uint8_t USART1_GetRxFlag(void)
{
if (USART1_RxFlag == 1)
{
USART1_RxFlag = 0;
return 1;
}
return 0;
}
//检查 USART1_RxFlag 是否为 1,如果是则将其重置为 0,并返回 1,表示接收到数据。
uint8_t USART1_GetRxData(void)
{
return USART1_RxData;
}//返回接收到的串口数据 USART1_RxData。
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
USART1_RxData = USART_ReceiveData(USART1);
USART1_RxFlag = 1;
USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除中断挂起位。
}
}
//在中断处理函数中,当接收到串口数据(USART_IT_RXNE 标志位被设置为 SET)时,获取接收到的数据并设置 USART1_RxFlag 为 1。
语言控制部分(与上文代码大体结构相同,上文代码中均有解释,此段代码不做解释,参考上文即可理解)
#include "stm32f10x.h" // Device header
#include <stdio.h>
#include <stdarg.h>
#include "Delay.h"
#include <stdlib.h>
uint8_t USART3_RxData;
uint8_t USART3_RxFlag;
void USART3_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_Init(GPIOB, &GPIO_InitStructure);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART3, &USART_InitStructure);
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_Init(&NVIC_InitStructure);
USART_Cmd(USART3, ENABLE);
}
uint8_t USART3_GetRxFlag(void)
{
if (USART3_RxFlag == 1)
{
USART3_RxFlag = 0;
return 1;
}
return 0;
}
uint8_t USART3_GetRxData(void)
{
return USART3_RxData;
}
void USART3_IRQHandler(void)
{
if (USART_GetITStatus(USART3, USART_IT_RXNE) == SET)
{
USART3_RxData = USART_ReceiveData(USART3);
USART3_RxFlag = 1;
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
}
}
OLED部分
因内容太多,放于下一期文章,本文侧重点在于控制系统
舵机部分
舵机原理见(【STM32(意法半导体推出的32位微控制器)通过HC-05主从机一体蓝牙模块实现对舵机(SG90)的控制(含源码) - CSDN App】http://t.csdnimg.cn/O6hDl)
PWM、Servo、Movement三个文件共同为驱动舵机服务
PA6:右前脚,PA7:右后脚,PB0:左前脚,PB1:左后脚
PWM
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
// 使能 TIM3 的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 使能 GPIOA 的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 使能 GPIOB 的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// GPIO 初始化结构体设置
GPIO_InitTypeDef GPIO_InitStructure;
// 设置 GPIO 模式为复用推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
// 设置 GPIO 引脚为 PA6 和 PA7
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
// 设置 GPIO 速度为 50MHz
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 初始化 GPIOA
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 设置 GPIO 引脚为 PB0 和 PB1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
// 初始化 GPIOB
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置 TIM3 内部时钟
TIM_InternalClockConfig(TIM3);
// TIM 时间基准初始化结构体设置
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
// 设置时钟分频为 1
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
// 设置计数器模式为向上计数
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
// 设置周期为 20000 - 1
TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;
// 设置预分频器为 72 - 1
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;
// 设置重复计数器为 0
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
// 初始化 TIM3 的时间基准
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
// TIM 输出比较初始化结构体设置
TIM_OCInitTypeDef TIM_OCInitStructure;
// 结构体初始化
TIM_OCStructInit(&TIM_OCInitStructure);
// 设置输出比较模式为 PWM1
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
// 设置输出极性为高
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
// 设置输出使能为启用
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
// 设置脉冲宽度为 0
TIM_OCInitStructure.TIM_Pulse = 0;
// 初始化 TIM3 的通道 2
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
// 初始化 TIM3 的通道 3
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
// 初始化 TIM3 的通道 1
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
// 初始化 TIM3 的通道 4
TIM_OC4Init(TIM3, &TIM_OCInitStructure);
// 使能 TIM3
TIM_Cmd(TIM3, ENABLE);
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM3, Compare);
}
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM3, Compare);
}
void PWM_SetCompare3(uint16_t Compare)
{
TIM_SetCompare3(TIM3, Compare);
}
void PWM_SetCompare4(uint16_t Compare)
{
TIM_SetCompare4(TIM3, Compare);
}
//设置比较值的主要目的是控制 PWM(脉冲宽度调制)信号的占空比。
通过改变比较值,可以调整输出信号高电平的持续时间,从而实现对被控对象的精确控制,比如调节电机的转速、灯光的亮度等。不同的比较值对应着不同的占空比,进而影响输出的效果。
SERVO
Angle 是舵机角度,范围:0~180
#include "stm32f10x.h" // Device header // 包含 STM32 芯片相关的头文件
#include "PWM.h" // 包含 PWM 相关的头文件
void Servo_Init(void) // 定义 Servo_Init 函数
{
PWM_Init(); // 调用 PWM_Init 函数进行初始化
}
void Servo_SetAngle1(float Angle) // 定义 Servo_SetAngle1 函数,用于设置角度 1
{
PWM_SetCompare1(Angle / 180 * 2000 + 500); // 根据输入的角度计算比较值并设置
}
void Servo_SetAngle2(float Angle) // 定义 Servo_SetAngle2 函数,用于设置角度 2
{
PWM_SetCompare2(Angle / 180 * 2000 + 500); // 同上
}
void Servo_SetAngle3(float Angle) // 定义 Servo_SetAngle3 函数,用于设置角度 3
{
PWM_SetCompare3(Angle / 180 * 2000 + 500); // 同上
}
void Servo_SetAngle4(float Angle) // 定义 Servo_SetAngle4 函数,用于设置角度 4
{
PWM_SetCompare4(Angle / 180 * 2000 + 500); // 同上
}
uint8_t Servo_GetAngle1(void) // 定义 Servo_GetAngle1 函数,用于获取角度 1
{
return (TIM_GetCapture1(TIM3) - 500) * 180 / 2000; // 通过定时器捕获值计算并返回角度
}
uint8_t Servo_GetAngle2(void) // 定义 Servo_GetAngle2 函数,用于获取角度 2
{
return (TIM_GetCapture2(TIM3) - 500) * 180 / 2000; // 同上
}
uint8_t Servo_GetAngle3(void) // 定义 Servo_GetAngle3 函数,用于获取角度 3
{
return (TIM_GetCapture3(TIM3) - 500) * 180 / 2000; // 同上
}
uint8_t Servo_GetAngle4(void) // 定义 Servo_GetAngle4 函数,用于获取角度 4
{
return (TIM_GetCapture4(TIM3) - 500) * 180 / 2000; // 同上
}
//这样的计算是为了将角度范围转换为相应的定时器比较值,从而实现对 PWM 信号占空比的精确控制,以达到控制伺服o 电机转动角度的目的。
MOVEMENT(此段函数是设置一些角度)
#include "stm32f10x.h" // Device header
#include "Servo.h"
#include "PWM.h"
#include "Delay.h"
#include "stdlib.h"
int i,j;
int movedelay=150;
uint8_t angle_1, angle_2, angle_3, angle_4;
void move_stand(void){
Servo_SetAngle1(90);
Servo_SetAngle2(90);
Servo_SetAngle3(90);
Servo_SetAngle4(90);
Delay_ms(500);
}
void move_forward(void){
Servo_SetAngle1(135);
Servo_SetAngle4(45);
Delay_ms(movedelay);
Servo_SetAngle2(45);
Servo_SetAngle3(135);
Delay_ms(movedelay);
Servo_SetAngle1(90);
Servo_SetAngle4(90);
Delay_ms(movedelay);
Servo_SetAngle2(90);
Servo_SetAngle3(90);
Delay_ms(movedelay);
Servo_SetAngle2(135);
Servo_SetAngle3(45);
Delay_ms(movedelay);
Servo_SetAngle1(45);
Servo_SetAngle4(135);
Delay_ms(movedelay);
Servo_SetAngle2(90);
Servo_SetAngle3(90);
Delay_ms(movedelay);
Servo_SetAngle1(90);
Servo_SetAngle4(90);
Delay_ms(movedelay);
}
void move_behind(void){
Servo_SetAngle1(45);
Servo_SetAngle4(135);
Delay_ms(movedelay);
Servo_SetAngle2(135);
Servo_SetAngle3(45);
Delay_ms(movedelay);
Servo_SetAngle1(90);
Servo_SetAngle4(90);
Delay_ms(movedelay);
Servo_SetAngle2(90);
Servo_SetAngle3(90);
Delay_ms(movedelay);
Servo_SetAngle2(45);
Servo_SetAngle3(135);
Delay_ms(movedelay);
Servo_SetAngle1(135);
Servo_SetAngle4(45);
Delay_ms(movedelay);
Servo_SetAngle2(90);
Servo_SetAngle3(90);
Delay_ms(movedelay);
Servo_SetAngle1(90);
Servo_SetAngle4(90);
Delay_ms(movedelay);
}
void move_right(void){
Servo_SetAngle1(45);
Servo_SetAngle4(45);
Delay_ms(movedelay);
Servo_SetAngle2(135);
Servo_SetAngle3(135);
Delay_ms(movedelay);
Servo_SetAngle1(90);
Servo_SetAngle4(90);
Delay_ms(movedelay);
Servo_SetAngle2(90);
Servo_SetAngle3(90);
Delay_ms(movedelay);
}
void move_left(void)
{
Servo_SetAngle2(135);
Servo_SetAngle3(135);
Delay_ms(movedelay);
Servo_SetAngle1(45);
Servo_SetAngle4(45);
Delay_ms(movedelay);
Servo_SetAngle2(90);
Servo_SetAngle3(90);
Delay_ms(movedelay);
Servo_SetAngle1(90);
Servo_SetAngle4(90);
Delay_ms(movedelay);
}
void move_hello(void)
{
for(i=0;i<20;i++)//
{
Servo_SetAngle1(90-i);
Servo_SetAngle2(90+i);
Servo_SetAngle4(90-i);
Delay_ms(4);
}
for(i=0;i<60;i++)
{
Servo_SetAngle2(110+i);
Servo_SetAngle4(70-i);
Delay_ms(4);
}
for(i=0;i<60;i++)
{
Servo_SetAngle1(70+i);
Delay_ms(4);
}
Delay_ms(50);
Servo_SetAngle1(180);
Delay_ms(500);
Servo_SetAngle1(130);
Delay_ms(500);
Servo_SetAngle1(180);
Delay_ms(500);
Servo_SetAngle1(130);
Delay_ms(500);
Servo_SetAngle1(70);
Delay_ms(500);
}
void move_shake_qianhou(void)
{
Servo_SetAngle2(135);
Servo_SetAngle1(135);
Servo_SetAngle3(45);
Servo_SetAngle4(45);
Delay_ms(150);
Servo_SetAngle1(90);
Servo_SetAngle2(90);
Servo_SetAngle3(90);
Servo_SetAngle4(90);
Delay_ms(150);
Servo_SetAngle2(45);
Servo_SetAngle1(45);
Servo_SetAngle3(135);
Servo_SetAngle4(135);
Delay_ms(150);
Servo_SetAngle1(90);
Servo_SetAngle2(90);
Servo_SetAngle3(90);
Servo_SetAngle4(90);
Delay_ms(150);
}
void move_shake_zuoyou(void)
{
Servo_SetAngle1(135);
Servo_SetAngle2(135);
Delay_ms(movedelay);
Servo_SetAngle3(135);
Servo_SetAngle4(135);
Delay_ms(movedelay);
Servo_SetAngle1(90);
Servo_SetAngle2(90);
Delay_ms(movedelay);
Servo_SetAngle3(90);
Servo_SetAngle4(90);
Delay_ms(movedelay);
Servo_SetAngle3(45);
Servo_SetAngle4(45);
Delay_ms(movedelay);
Servo_SetAngle1(45);
Servo_SetAngle2(45);
Delay_ms(movedelay);
Servo_SetAngle3(90);
Servo_SetAngle4(90);
Delay_ms(movedelay);
Servo_SetAngle1(90);
Servo_SetAngle2(90);
Delay_ms(movedelay);
}
void move_dance(void)
{
Servo_SetAngle1(135);
Servo_SetAngle2(135);
Delay_ms(movedelay);
Servo_SetAngle3(135);
Servo_SetAngle4(135);
Delay_ms(movedelay);
Servo_SetAngle1(90);
Servo_SetAngle2(90);
Delay_ms(movedelay);
Servo_SetAngle3(90);
Servo_SetAngle4(90);
Delay_ms(movedelay);
Servo_SetAngle2(135);
Servo_SetAngle1(135);
Servo_SetAngle3(45);
Servo_SetAngle4(45);
Delay_ms(150);
Servo_SetAngle1(90);
Servo_SetAngle2(90);
Servo_SetAngle3(90);
Servo_SetAngle4(90);
Delay_ms(150);
Servo_SetAngle3(45);
Servo_SetAngle4(45);
Delay_ms(movedelay);
Servo_SetAngle1(45);
Servo_SetAngle2(45);
Delay_ms(movedelay);
Servo_SetAngle3(90);
Servo_SetAngle4(90);
Delay_ms(movedelay);
Servo_SetAngle1(90);
Servo_SetAngle2(90);
Delay_ms(movedelay);
Servo_SetAngle2(45);
Servo_SetAngle1(45);
Servo_SetAngle3(135);
Servo_SetAngle4(135);
Delay_ms(150);
Servo_SetAngle1(90);
Servo_SetAngle2(90);
Servo_SetAngle3(90);
Servo_SetAngle4(90);
Delay_ms(150);
}
void move_head_up(void)
{
for(i=0;i<20;i++)
{
Servo_SetAngle1(90-i);
Servo_SetAngle3(90+i);
Delay_ms(10);
}
for(i=0;i<65;i++)
{
Servo_SetAngle2(90+i);
Servo_SetAngle4(90-i);
Delay_ms(10);
}
}
void move(uint16_t set1 , uint16_t set2 , uint16_t set3 , uint16_t set4 , uint16_t speed){
angle_1 = Servo_GetAngle1();
angle_2 = Servo_GetAngle2();
angle_3 = Servo_GetAngle3();
angle_4 = Servo_GetAngle4();
while (angle_1 != set1 || angle_3 != set3 || angle_2 != set2 || angle_4 != set4) {
if (angle_1 > set1) {
--angle_1;
Servo_SetAngle1(angle_1);
} else if (angle_1 < set1) {
++angle_1;
Servo_SetAngle1(angle_1);
}
if (angle_3 > set3) {
--angle_3;
Servo_SetAngle3(angle_3);
} else if (angle_3 < set3) {
++angle_3;
Servo_SetAngle3(angle_3);
}
if (angle_2 > set2) {
--angle_2;
Servo_SetAngle2(angle_2);
} else if (angle_2 < set2) {
++angle_2;
Servo_SetAngle2(angle_2);
}
if (angle_4 > set4) {
--angle_4;
Servo_SetAngle4(angle_4);
} else if (angle_4 < set4) {
++angle_4;
Servo_SetAngle4(angle_4);
}
Delay_ms(1000/speed);
}
}
void move_slow_stand(uint8_t previous_mode){
if (previous_mode == '0')
return;
angle_1 = Servo_GetAngle1();
angle_2 = Servo_GetAngle2();
angle_3 = Servo_GetAngle3();
angle_4 = Servo_GetAngle4();
while (angle_1 != 90 || angle_3 != 90 || angle_2 != 90 || angle_4 != 90) {
if (angle_1 > 90) {
--angle_1;
Servo_SetAngle1(angle_1);
} else if (angle_1 < 90) {
++angle_1;
Servo_SetAngle1(angle_1);
}
//
if (angle_3 > 90) {
--angle_3;
Servo_SetAngle3(angle_3);
} else if (angle_3 < 90) {
++angle_3;
Servo_SetAngle3(angle_3);
}
if (angle_2 > 90) {
--angle_2;
Servo_SetAngle2(angle_2);
} else if (angle_2 < 90) {
++angle_2;
Servo_SetAngle2(angle_2);
}
//
if (angle_4 > 90) {
--angle_4;
Servo_SetAngle4(angle_4);
} else if (angle_4 < 90) {
++angle_4;
Servo_SetAngle4(angle_4);
}
Delay_ms(10);
}
}
void move_stretch(void){
for(i=0;i<65;i++)
{
Servo_SetAngle2(90+i);
Servo_SetAngle4(90-i);
Delay_ms(5);
}
for(i=0;i<20;i++)
{
Servo_SetAngle1(90-i);
Servo_SetAngle3(90+i);
Delay_ms(5);
}
Delay_ms(1000);
for(i=0;i<60;i++)
{
Servo_SetAngle1(70+i);
Delay_ms(4);
}
Delay_ms(1000);
Servo_SetAngle1(180);
Delay_ms(500);
Servo_SetAngle1(130);
Delay_ms(500);
Servo_SetAngle1(180);
Delay_ms(500);
Servo_SetAngle1(130);
Delay_ms(500);
Servo_SetAngle1(70);
Delay_ms(5);
}
void move_two_hands(void){
Servo_SetAngle3(20);
Servo_SetAngle2(20);
Delay_ms(200);
Servo_SetAngle3(90);
Servo_SetAngle2(90);
Delay_ms(200);
Servo_SetAngle1(160);
Servo_SetAngle4(160);
Delay_ms(200);
Servo_SetAngle1(90);
Servo_SetAngle4(90);
Delay_ms(200);
}
void lan_yao(void){
for(i=0;i<75;i++)
{
Servo_SetAngle1(90+i);
Servo_SetAngle3(90-i);
Servo_SetAngle2(90+i/2);
Servo_SetAngle4(90-i/2);
Delay_ms(5);
}
Delay_ms(movedelay*50);
for(i=0;i<75;i++)
{
Servo_SetAngle1(165-i);
Servo_SetAngle3(15+i);
Servo_SetAngle2(127-i/2);
Servo_SetAngle4(53+i/2);
Delay_ms(5);
}
Delay_ms(movedelay);
}
void move_sleep_p(void) {
for(i=0;i<75;i++)
{
Servo_SetAngle1(90+i);
Servo_SetAngle3(90-i);
Delay_ms(10);
}
for(i=0;i<75;i++)
{
Servo_SetAngle4(90+i);
Servo_SetAngle2(90-i);
Delay_ms(10);
}
}
void move_sleep_w(void) {
for(i=0;i<75;i++)
{
Servo_SetAngle3(90+i);
Servo_SetAngle1(90-i);
Delay_ms(10);
}
for(i=0;i<75;i++)
{
Servo_SetAngle2(90+i);
Servo_SetAngle4(90-i);
Delay_ms(10);
}
}
各器件配合部分(各种器件连在一起完成某个动作)
#include "stm32f10x.h" // Device header
#include "stdlib.h"
#include "LED.h"
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "PWM.h"
#include "Movement.h"
#include "usart1.h"
#include "usart3.h"
#include "stdio.h"
extern uint8_t move_mode1 ;
extern uint8_t move_mode3 ;
extern uint8_t move_mode ;
extern uint8_t previous_mode ;
void mode_forward(void)
{
OLED_ShowImage(0, 0, 128, 64, BMP2);
move_forward();
LED13_Turn();
previous_mode = move_mode;
}
void mode_behind(void)
{
OLED_ShowImage(0, 0, 128, 64, BMP2);
move_behind();
LED13_Turn();
previous_mode = move_mode;
}
void mode_left(void)
{
OLED_ShowImage(0, 0, 128, 64, BMP3);
move_left();
LED13_Turn();
previous_mode = move_mode;
}
void mode_right(void)
{
OLED_ShowImage(0, 0, 128, 64, BMP4);
move_right();
LED13_Turn();
previous_mode = move_mode;
}
void mode_swing_qianhou(void)
{
OLED_ShowImage(0, 0, 128, 64, BMP11);
move_shake_qianhou();
LED13_Turn();
previous_mode = move_mode;
}
void mode_swing_zuoyou(void)
{
OLED_ShowImage(0, 0, 128, 64, BMP11);
move_shake_zuoyou();
LED13_Turn();
previous_mode = move_mode;
}
void mode_dance(void)
{
OLED_ShowImage(0, 0, 128, 64, BMP5);
move_dance();
LED13_Turn();
previous_mode = move_mode;
}
void mode_stand(void)
{
OLED_ShowImage(0, 0, 128, 64, BMP1);
move_stand();
LED13_Turn();
previous_mode = move_mode;
move_mode = '0';
}
void mode_slowstand(void)
{
OLED_ShowImage(0, 0, 128, 64, BMP1);
move_slow_stand(previous_mode);
LED13_Turn();
previous_mode = move_mode;
move_mode = '0';
}
void mode_strech(void)
{
OLED_ShowImage(0, 0, 128, 64, BMP1);
move_slow_stand(previous_mode);
OLED_ShowImage(0, 0, 128, 64, BMP2);
move_stretch();
OLED_ShowImage(0, 0, 128, 64, BMP12);
LED13_Turn();
previous_mode = move_mode;
move_mode = '0';
}
void mode_hello(void)
{
if (previous_mode != '5' && previous_mode != 'D') {
OLED_ShowImage(0, 0, 128, 64, BMP1);
move_slow_stand(previous_mode);
}
OLED_ShowImage(0, 0, 128, 64, BMP12);
int i;
for(i=0;i<20;i++)
{
Servo_SetAngle1(90-i);
Servo_SetAngle2(90+i);
Servo_SetAngle4(90-i);
Delay_ms(7);
}
for(i=0;i<40;i++)
{
Servo_SetAngle2(110+i);
Servo_SetAngle4(70-i);
Delay_ms(7);
}
for(i=0;i<60;i++)
{
Servo_SetAngle1(70+i);
Delay_ms(4);
}
Delay_ms(50);
Servo_SetAngle1(180);
Delay_ms(400);
Servo_SetAngle1(130);
Delay_ms(400);
Servo_SetAngle1(180);
Delay_ms(400);
Servo_SetAngle1(130);
Delay_ms(400);
Servo_SetAngle1(70);
Delay_ms(500);
LED13_Turn();
previous_mode = move_mode;
move_mode = '0';
}
void mode_twohands(void)
{
OLED_ShowImage(0, 0, 128, 64, BMP1);
move_stand();
move_two_hands();
LED13_Turn();
previous_mode = move_mode;
move_mode = '0';
}
void mode_lanyao(void)
{
OLED_ShowImage(0, 0, 128, 64, BMP1);
move_slow_stand(previous_mode);
OLED_ShowImage(0, 0, 128, 64, BMP9);
lan_yao();
OLED_ShowImage(0, 0, 128, 64, BMP1);
LED13_Turn();
previous_mode = move_mode;
move_mode = '0';
}
void mode_headup(void)
{
OLED_ShowImage(0, 0, 128, 64, BMP1);
move_slow_stand(previous_mode);
OLED_ShowImage(0, 0, 128, 64, BMP10);
move_head_up();
LED13_Turn();
previous_mode = move_mode;
move_mode = '0';
}
void mode_sleeppa(void)
{
if (previous_mode != '5' && previous_mode != 'q') {
OLED_ShowImage(0, 0, 128, 64, BMP1);
move_slow_stand(previous_mode);
}
if (rand()%2) {
OLED_ShowImage(0, 0, 128, 64, BMP6);
}
else{
OLED_ShowImage(0, 0, 128, 64, BMP8);
}
move_sleep_p();
previous_mode = move_mode;
move_mode = '0';
}
void mode_sleepwo(void)
{
if (previous_mode != '5' && previous_mode != 'q') {
OLED_ShowImage(0, 0, 128, 64, BMP1);
move_slow_stand(previous_mode);
Delay_s(1);
}
if (rand()%2) {
OLED_ShowImage(0, 0, 128, 64, BMP6);
}
else{
OLED_ShowImage(0, 0, 128, 64 , BMP8);
}
move_sleep_w();
previous_mode = move_mode;
move_mode = '0';
}
主函数逻辑
#include "stm32f10x.h" // 包含 STM32 芯片的头文件
#include "stdlib.h"
#include <stdio.h>
#include "LED.h"
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "PWM.h"
#include "Movement.h"
#include "usart1.h"
#include "usart3.h"
#include "Mode.h"
// 定义变量
uint8_t RxData; // 接收数据变量
uint8_t move_mode1 = '0'; // 模式 1
uint8_t move_mode3 = '0'; // 模式 3
uint8_t move_mode = '0'; // 当前模式
uint8_t previous_mode = '0'; // 上一个模式
int main(void) // 主函数
{
LED_Init(); // 初始化 LED
OLED_Init(); // 初始化 OLED
USART1_Init(); // 初始化 USART1
USART3_Init(); // 初始化 USART3
Servo_Init(); // 初始化舵机
OLED_Clear(); // 清屏
OLED_ShowImage(0, 0, 128, 64, BMP1); // 显示图像
mode_stand(); // 进入站立模式
while (1) // 主循环
{
move_mode1 = USART1_GetRxData(); // 获取 USART1 接收的数据
move_mode3 = USART3_GetRxData(); // 获取 USART3 接收的数据
if (USART1_GetRxFlag()) // 如果 USART1 有接收标志
{
move_mode = move_mode1; // 更新当前模式为模式 1
}
else if (USART3_GetRxFlag()) // 如果 USART3 有接收标志
{
move_mode = move_mode3; // 更新当前模式为模式 3
}
if (move_mode == 'f') // 如果当前模式为前进
{
mode_forward(); // 执行前进模式
}
else if (move_mode == 'b') // 如果当前模式为后退
{
mode_behind(); // 执行后退模式
}
else if (move_mode == 'l') // 如果当前模式为左移
{
mode_left(); // 执行左移模式
}
else if (move_mode == 'r') // 如果当前模式为右移
{
mode_right(); // 执行右移模式
}
else if (move_mode == 'w') // 如果当前模式为前后摆动
{
mode_swing_qianhou(); // 执行前后摆动模式
}
else if (move_mode == 'z') // 如果当前模式为左右摆动
{
mode_swing_zuoyou(); // 执行左右摆动模式
}
else if (move_mode == 'd') // 如果当前模式为舞蹈
{
mode_dance(); // 执行舞蹈模式
}
else if (move_mode == '5') // 如果当前模式为站立
{
mode_stand(); // 执行站立模式
}
else if (move_mode == 'q' && previous_mode!= '0') // 如果当前模式为缓慢站立且上一个模式不为 0
{
mode_slowstand(); // 执行缓慢站立模式
}
else if (move_mode =='s' && previous_mode!= '') // 如果当前模式为伸展且上一个模式不为伸展
{
mode_strech(); // 执行伸展模式
}
else if (move_mode == 'j') // 如果当前模式为双手
{
mode_twohands(); // 执行双手模式
}
else if (move_mode == 'y') // 如果当前模式为懒腰
{
mode_lanyao(); // 执行懒腰模式
}
else if (move_mode == '1') // 如果当前模式为抬头
{
mode_headup(); // 执行抬头模式
}
else if (move_mode == 'p' && previous_mode!= 'p') // 如果当前模式为睡眠且上一个模式不为睡眠
{
mode_sleeppa(); // 执行睡眠模式
}
else if (move_mode == '2' && previous_mode!= '2') // 如果当前模式为另一种睡眠且上一个模式不为另一种睡眠
{
mode_sleepwo(); // 执行另一种睡眠模式
}
}
}
LD3320串口版语音模块(需单独烧录)
放置开源代码中,不进行解释,可参考文章(http://【STM32F103C8T6与LD3320进行串口通讯控制LED灯的亮灭 - CSDN App】http://t.csdnimg.cn/8qo2F)
开源代码
通过网盘分享的文件:软件.zip
链接: https://pan.baidu.com/s/1lWq7ZRIU3ooZQUHlGB4-mg?pwd=2003 提取码: 2003
版权归原作者 CV工程狮 所有, 如有侵权,请联系我们删除。