嘉立创产业服务站群
发作品签到
专业版

基于梁山派开发板的蓝牙遥控小车

工程标签

1.7k
0
0
0

简介

基于梁山派开发板的蓝牙遥控小车

简介:基于梁山派开发板的蓝牙遥控小车

开源协议

GPL 3.0

创建时间:2022-11-18 00:37:18更新时间:2022-12-21 01:44:12

描述

该智能小车能够使用超声波测距并在OLED屏上显示信息,如果前方物体距离小于10cm蜂鸣器会发出警报,可以通过蓝牙模块来控制小车前进、后退、左旋转、右旋转,小车前方的led灯会根据是哪个方向的旋转亮灯,小车还能进行对电池adc电压检测,并在屏幕显示,蓝牙控制是手机上到HC的官网下载HC蓝牙助手然后自定义按钮发送数据到HC_05蓝牙模块上,利用开发板的串口接收完成对发送数据的接收,然后做出相应动作(目前只能完成基本的操作后续会对代码进行改进)。
第一次的设计存在一些错误,错误的地方现在已经修改,修改如下:一、的蜂鸣器的地接错位置,经过修改后,在原来的板子上进行了飞线处理,经过验证可正常使用。二、原来板子利用了PA10来控制电机芯片,因为PA10是下载引脚,会导致一上电电机就会转动,还会导致有时候程序下载不进去,则将PA10改成PB6.

一、项目简介

基于梁山派开发板的蓝牙遥控小车

二、功能介绍

功能:
1.利用蓝牙控制小车,使用的是hc_05模块,手机下载hc的官方的控制软件。
2.OLED屏幕显示小车前方物体的距离、前进的速度挡位、电池电压的adc。
3.超声波避障。
4.蜂鸣器报警。
5.车灯在左转和右转分别显示不同的状态,并且开发板上的LED会根据方向来进行流水灯显示。
6.按键控制小车前进时的挡位。

电路分析

1.电源模块

QQ截图20221220103913.png
利用两节18650电池串联,先经过一个稳压二极管进行稳压,然后经过开关,可输出为驱动电机的电压,然后经过降压芯片输出5V的电压,其中还分别并联两个钽电容进行滤波。

2.蜂鸣器模块

QQ截图20221220105047.png
当BUZZER引脚输出高电平,蜂鸣器即开,其中引脚选择了PB4,可以配置为定时器输出,该代码已配置为定时器输出,可通过改变输入值来控制发出的声音的频率。

#include "bsp_buzzer.h"
void buzzer_config(void)
{
	/* 使能时钟 */
	rcu_periph_clock_enable(RCU_BUZZER);
	/* 配置为输出模式 复用模式 */
	gpio_mode_set(PORT_BUZZER,GPIO_MODE_AF,GPIO_PUPD_NONE,PIN_BUZZER);
	/* 配置为推挽输出 50MHZ */
	gpio_output_options_set(PORT_BUZZER,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,PIN_BUZZER);
	gpio_af_set(PORT_BUZZER,GPIO_AF_2,PIN_BUZZER);//配置GPIO的复用
}
void buzzer_on(uint16_t speed)
{
	gpio_mode_set(PORT_BUZZER,GPIO_MODE_AF,GPIO_PUPD_NONE,PIN_BUZZER);	//复用推挽输出
	timer_channel_output_pulse_value_config(BSP_PWM2_TIMER,BSP_PWM0_CHANNEL_0,speed); // 配置定时器通道输出脉冲值

}

3.按键电路

QQ截图20221220105838.png
把key引脚设置为上拉模式,如果按键按下则将引脚电平拉低,该代码且将开发板上的key引脚一起初始化了,并且能够在OLED屏上显示速度的挡位。

#include "bsp_key.h"
#include "sys.h"
#include "bsp_led.h"
#include "stdio.h"

void key_gpio_config(void)
{
	/*开启GPIO时钟*/ 
	rcu_periph_clock_enable(BSP_KEY_RCU);
	rcu_periph_clock_enable(RCU_SYSCFG);
	rcu_periph_clock_enable(BSP_KEY1_RCU);
	/*配置GPIO时钟模式*/
	gpio_mode_set( BSP_KEY_PORT, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, BSP_KEY_PIN);
	
	gpio_mode_set(BSP_KEY1_PORT,GPIO_MODE_INPUT,GPIO_PUPD_PULLUP,BSP_KEY1_PIN);  // 按键默认状态是高电平,配置为上拉
	nvic_irq_enable(BSP_KEY_EXTI_IRQN,3U, 3U); 
	syscfg_exti_line_config(BSP_KEY_EXTI_PORT_SOURCE,BSP_KEY_EXTI_PIN_SOURCE );
	
	exti_init(BSP_KEY_EXTI_LINE, EXTI_INTERRUPT, EXTI_TRIG_BOTH);
	exti_interrupt_enable(BSP_KEY_EXTI_LINE);
	exti_interrupt_flag_get(BSP_KEY_EXTI_LINE);
}
void BSP_KEY_EXTI_IRQHANDLER(void)              			//函数不需要被调用,	不占用CPU时间
{
	if(exti_interrupt_flag_get(BSP_KEY_EXTI_LINE)==SET)
	{
		if(gpio_input_bit_get(BSP_KEY_PORT, BSP_KEY_PIN)==SET)
		{
			printf("key press!\r\n");
		}
		else
		{
			printf("key release!\r\n");
		}
		exti_interrupt_flag_clear(BSP_KEY_EXTI_LINE);
	}

}
void key_scan(void)
{
	/*获取按键引脚对应状态*/
	if(gpio_input_bit_get(BSP_KEY_PORT, BSP_KEY_PIN)==SET)
	{
		delay_1ms(20);
		if(gpio_input_bit_get(BSP_KEY_PORT, BSP_KEY_PIN)==SET)
		{
			gpio_bit_toggle(PORT_LED0,PIN_LED0);
			printf("key press!/r/n");
			while(gpio_input_bit_get(BSP_KEY_PORT, BSP_KEY_PIN)==SET);
			printf("key release!/r/n");
		}
	}
}

uint8_t gearshift=1;
uint8_t KEY_Read(void)
{
	
    /* 先读取按键引脚的电平 如果低电平,按键按下 */
    if(gpio_input_bit_get(BSP_KEY1_PORT,BSP_KEY1_PIN) == RESET)         // 按键按下
    {
        delay_1ms(20);  // 延迟消抖 
        if(gpio_input_bit_get(BSP_KEY1_PORT,BSP_KEY1_PIN) == RESET)       // 再次检测按键是否按下 
			{
				gearshift++;
				if(gearshift>3)
				{
					gearshift=1;
					
				}
			}
    } 
	
	return 	gearshift;
}
void gearshift_show(void)
{
	uint16_t a;
	a=KEY_Read();
	OLED_ShowString(1,3,"speed:",2);
	OLED_ShowNum(60,3,a,1,2);
}

#ifndef _BSP_KEY_H
#define _BSP_KEY_H

#include "gd32f4xx.h"
#include "systick.h"
#include "bsp_oled.h"

#define BSP_KEY_RCU RCU_GPIOA
#define BSP_KEY_PORT GPIOA
#define BSP_KEY_PIN GPIO_PIN_0

#define BSP_KEY1_RCU     RCU_GPIOB      // 按键端口时钟
#define BSP_KEY1_PORT    GPIOB          // 按键端口
#define BSP_KEY1_PIN     GPIO_PIN_3     // 按键引脚

#define BSP_KEY_EXTI_IRQN        EXTI0_IRQn
#define BSP_KEY_EXTI_PORT_SOURCE EXTI_SOURCE_GPIOA
#define BSP_KEY_EXTI_PIN_SOURCE  EXTI_SOURCE_PIN0 
#define BSP_KEY_EXTI_LINE		 EXTI_0
#define BSP_KEY_EXTI_IRQHANDLER  EXTI0_IRQHandler

void key_gpio_config(void);
void key_scan(void);
uint8_t KEY_Read(void);
void gearshift_show(void);

#endif

4.超声波测距

QQ截图20221220110715.png
使用了hc_04模块,测出小车前方物体距离并在OLED屏上显示。

#include "bsp_ultrasonsic.h"
#include "stdio.h"
uint8_t Count_update;
static void ultrasonsic_gpio_config(void)            				//static只有文件下方可以调用,其他地方不能使用
{
	/*开启GPIOB时钟*/ 
	rcu_periph_clock_enable(BSP_ULTRASONSIC_RCU);
	/*配置GPIOB时钟模式*/
	gpio_mode_set( BSP_ULTRASONSIC_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, BSP_ULTRASONSIC_TRIG_PIN);	//浮空输出
	/*配置GPIOB时钟输出*/
	gpio_output_options_set(BSP_ULTRASONSIC_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, BSP_ULTRASONSIC_TRIG_PIN);//ULTRASONSIC_TRAG推挽输出

	gpio_mode_set( BSP_ULTRASONSIC_PORT, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, BSP_ULTRASONSIC_ECHO_PIN);	//下拉输入
	gpio_bit_write(BSP_ULTRASONSIC_PORT,BSP_ULTRASONSIC_TRIG_PIN,RESET);//TRAG初始状态为低电平	
}
static void Ultras_TIM2_Init(uint16_t pre,uint16_t per)
{
	timer_parameter_struct timere_initpara;
	rcu_periph_clock_enable(BSP_ULTRASONSIC_TIMER_RCU);
	rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
	timer_deinit(BSP_ULTRASONSIC);
	timere_initpara.prescaler =pre-1;                         /*!< prescaler value */
    timere_initpara.alignedmode =TIMER_COUNTER_EDGE;                       /*!< aligned mode */
    timere_initpara.counterdirection=TIMER_COUNTER_UP;                  /*!< counter direction */
    timere_initpara.clockdivision =TIMER_CKDIV_DIV1;                     /*!< clock division value */
    timere_initpara.period =per-1;                            /*!< period value */
    timere_initpara.repetitioncounter=0;
	timer_init(BSP_ULTRASONSIC, &timere_initpara);
	nvic_irq_enable(BSP_ULTRASONSIC_IRQ,3U, 2U);
	timer_interrupt_enable(BSP_ULTRASONSIC,TIMER_INT_UP);
	//timer_enable(BSP_ULTRASONSIC);
	timer_interrupt_flag_clear(BSP_ULTRASONSIC,TIMER_INT_FLAG_UP); //清除更新中断,免得一打开中断立即产生中断
	timer_break_enable(BSP_ULTRASONSIC);			 //打开定时器更新中断
	timer_disable(BSP_ULTRASONSIC);
}
void Ultrasonic_Init(void)
{
	ultrasonsic_gpio_config();
	Ultras_TIM2_Init(200,1000);				//1ms

}
void  BSP_ULTRASONSIC_IRAHANDLER(void)				//定时器中断,每1ms count+1
{
	if(timer_interrupt_flag_get(BSP_ULTRASONSIC,TIMER_INT_FLAG_UP)==SET)
	{
		timer_interrupt_flag_clear(BSP_ULTRASONSIC,TIMER_INT_FLAG_UP);
		Count_update++;
	}
}
static void timer_open(void)        //打开定时器
{
   Count_update = 0;
	timer_counter_value_config(BSP_ULTRASONSIC, 0);
	timer_enable(BSP_ULTRASONSIC);//使能定时器
}
static void timer_close(void)        //关闭定时器
{
	timer_disable(BSP_ULTRASONSIC);
}

uint32_t GetEchoTimer(void)
{
   uint32_t time= 0;
	/*//当回响信号很长是,计数值溢出后重复计数,overCount用中断来保存溢出次数*/
	time = Count_update*1000;//overCount每++一次,代表overCount毫秒,time微妙
	time+=timer_counter_read(BSP_ULTRASONSIC);//获取计TIM2数寄存器中的计数值,一边计算回响信号时间
	timer_counter_value_config(BSP_ULTRASONSIC,0); //将TIM2计数寄存器的计数值清零
	delay_1ms(50);
	return time;
}
float Hcsr04GetLength(void )
{
	/*测5次数据计算一次平均值*/
	float length = 0;
	float t = 0;
	float sum = 0;
	uint16_t i = 0;
	while(i != 5){
		TRIG=1;
		delay_1us(20);//持续时间超过10us
		TRIG=0;
		/*Echo发出信号 等待回响信号*/
		/*输入方波后,模块会自动发射8个40KHz的声波,与此同时回波引脚(echo)端的电平会由0变为1;
		(此时应该启动定时器计时);当超声波返回被模块接收到时,回波引 脚端的电平会由1变为0;
		(此时应该停止定时器计数),定时器记下的这个时间即为
			超声波由发射到返回的总时长;*/
		while(ECHO == 0);//echo等待回响
		/*开启定时器*/
		timer_open();
		i = i+1; //每收到一次回响信号+1,收到5次就计算均值
		while(ECHO == 1);
		/*关闭定时器*/
		timer_close();
		/*获取Echo高电平时间时间*/
		t = GetEchoTimer();
		length = (float)t/58;//单位时cm
		sum += length;		
	}
	length = sum/5;//五次平均值
	return length;
}
int a,b,c,d,e,f,g,h=0;
void bsp_distance_oled(void)
{
		d=Hcsr04GetLength();
		a=d/100;
		b=(d%100)/10;
		c=(d%10);
		OLED_Clear();
		OLED_ShowString(1,2,"distance:",3);
		OLED_ShowNum(80,2,a,1,3);
		OLED_ShowNum(86,2,b,1,3);
		OLED_ShowNum(92,2,c,1,3);
}
#include "bsp_oled.h"
#define BSP_ULTRASONSIC_RCU  RCU_GPIOD
#define BSP_ULTRASONSIC_PORT GPIOD

#define BSP_ULTRASONSIC_TRIG_PIN  GPIO_PIN_1
#define BSP_ULTRASONSIC_ECHO_PIN  GPIO_PIN_5

#define BSP_ULTRASONSIC_TIMER_RCU        RCU_TIMER4
#define BSP_ULTRASONSIC            TIMER4
#define BSP_ULTRASONSIC_IRQ        TIMER4_IRQn
#define BSP_ULTRASONSIC_IRAHANDLER TIMER4_IRQHandler

#define TRIG PDout(1)
#define ECHO PDin(5)

void Ultrasonic_Init(void);
void  BSP_ULTRASONSIC_IRAHANDLER(void);
void Ultrasonic_Init(void);
uint32_t GetEchoTimer(void);
float HCSR04_Get_Distance(void);
float Hcsr04GetLength(void );
void bsp_distance_oled(void);
#endif

5.蓝牙模块

使用了hc_05模块。
QQ截图20221220112426.png

#include "bsp_hc.h"
#include "stdio.h"

uint8_t     g_recv_buff_hc[USART_RECEIVE_LENGTH];   // 接收缓冲区
uint16_t     g_recv_length_hc = 0;                     // 接收数据长度
uint8_t        g_recv_complete_flag_hc = 0;              // 接收数据完成标志位
void uart6_gpio_config(uint32_t band_rate)
{
    /* 开启时钟 */
    rcu_periph_clock_enable(BSP_UART6_TX_RCU);   // 开启串口时钟
    rcu_periph_clock_enable(BSP_UART6_RX_RCU);   // 开启端口时钟
    rcu_periph_clock_enable(BSP_UART6_RCU);      // 开启端口时钟
    
    /* 配置GPIO复用功能 */
    gpio_af_set(BSP_UART6_TX_PORT,BSP_UART6_AF,BSP_UART6_TX_PIN);    
    gpio_af_set(BSP_UART6_RX_PORT,BSP_UART6_AF,BSP_UART6_RX_PIN);    
    
    /* 配置GPIO的模式 */
    /* 配置TX为复用模式 上拉模式 */
    gpio_mode_set(BSP_UART6_TX_PORT,GPIO_MODE_AF,GPIO_PUPD_PULLUP,BSP_UART6_TX_PIN);
    /* 配置RX为复用模式 上拉模式 */
    gpio_mode_set(BSP_UART6_RX_PORT, GPIO_MODE_AF,GPIO_PUPD_PULLUP,BSP_UART6_RX_PIN);
    
    /* 配置TX为推挽输出 50MHZ */
    gpio_output_options_set(BSP_UART6_TX_PORT,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,BSP_UART6_TX_PIN);
    /* 配置RX为推挽输出 50MHZ */
    gpio_output_options_set(BSP_UART6_RX_PORT,GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, BSP_UART6_RX_PIN);

    /* 配置串口的参数 */
    usart_deinit(BSP_UART6);                                 // 复位串口
    usart_baudrate_set(BSP_UART6,band_rate);                 // 设置波特率
    usart_parity_config(BSP_UART6,USART_PM_NONE);            // 没有校验位
    usart_word_length_set(BSP_UART6,USART_WL_8BIT);          // 8位数据位
    usart_stop_bit_set(BSP_UART6,USART_STB_1BIT);              // 1位停止位

    /* 使能串口 */
    usart_enable(BSP_UART6);                                   // 使能串口
    usart_transmit_config(BSP_UART6,USART_TRANSMIT_ENABLE);  // 使能串口发送
    usart_receive_config(BSP_UART6,USART_RECEIVE_ENABLE);    // 使能串口接收
    
    /* 中断配置 */
    nvic_irq_enable(BSP_UART6_IRQ, 2, 2);                      // 配置中断优先级
    usart_interrupt_enable(BSP_UART6,USART_INT_RBNE);         // 读数据缓冲区非空中断和溢出错误中断
    usart_interrupt_enable(BSP_UART6,USART_INT_IDLE);         // 空闲检测中断
}
void BSP_UART6_IRQHandler(void)
{
    if(usart_interrupt_flag_get(BSP_UART6,USART_INT_FLAG_RBNE) == SET)     // 接收缓冲区不为空
    {
        g_recv_buff_hc[g_recv_length_hc++] = usart_data_receive(BSP_UART6);      // 把接收到的数据放到缓冲区中
    }
    
    if(usart_interrupt_flag_get(BSP_UART6,USART_INT_FLAG_IDLE) == SET)     // 检测到帧中断
    {
        usart_data_receive(BSP_UART6);                                     // 必须要读,读出来的值不能要
        g_recv_buff_hc[g_recv_length_hc] = '\0';                                   // 数据接收完毕,数组结束标志
        g_recv_complete_flag_hc = 1;                                          // 接收完成 
    }
}
#ifndef _BSP_HC_H
#define _BSP_HC_H

#include "gd32f4xx.h"
#include "systick.h"

#define BSP_UART6_TX_RCU      RCU_GPIOF        // 串口6TX的端口时钟
#define BSP_UART6_RX_RCU     RCU_GPIOF        // 串口6RX的端口时钟
#define BSP_UART6_RCU         RCU_UART6        // 串口6的时钟
#define BSP_UART6_TX_PORT     GPIOF             // 串口TX的端口
#define BSP_UART6_RX_PORT     GPIOF             // 串口RX的端口
#define BSP_UART6_AF         GPIO_AF_8           // 串口6的复用功能
#define BSP_UART6_TX_PIN      GPIO_PIN_7        // 串口6TX的引脚
#define BSP_UART6_RX_PIN      GPIO_PIN_6           // 串口6RX的引脚
#define BSP_UART6                 UART6                // 串口6
#define BSP_UART6_IRQ             UART6_IRQn          // 串口6中断
#define BSP_UART6_IRQHandler      UART6_IRQHandler // 串口6中断服务函数

/* 串口缓冲区的数据长度 */
#define USART_RECEIVE_LENGTH  4096

extern uint8_t  g_recv_buff_hc[USART_RECEIVE_LENGTH]; // 接收缓冲区
extern uint16_t g_recv_length_hc;                       // 接收数据长度
extern uint8_t  g_recv_complete_flag_hc;                // 接收完成标志位

void uart6_gpio_config(uint32_t band_rate);     // 配置串口6
#endif

6.车灯显示

电路设计
QQ截图20221220112759.png
代码部分

#include "bsp_led.h"

void led_gpio_config(unsigned int RCU_LED,unsigned int PORT_LED,unsigned int PIN_LED)
{
	/*开启GPIOD时钟*/ 
	rcu_periph_clock_enable(RCU_LED);
	/*配置GPIOD时钟模式*/
	gpio_mode_set( PORT_LED, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, PIN_LED);
	/*配置GPIOD时钟输出*/
	gpio_output_options_set(PORT_LED, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, PIN_LED);

}
void led_left(void)
{
	
	PGout(7)=0;
	PAout(12)=0;			
    delay_1ms(100);      					
	PGout(3)=1;
	PAout(12)=1;			
	delay_1ms(100);		
	PGout(3)=0;
	PDout(7)=1;			
    delay_1ms(100);       
	PDout(7)=0;
	PEout(3)=1;
	PAout(12)=0;			
    delay_1ms(100);       
	PEout(3)=0;
	PAout(12)=1;			
  	delay_1ms(100);
}
void led_right(void)
{
	PAout(12)=0;
	PGout(7)=0;
	delay_1ms(100);
	PEout(3)=1;
	delay_1ms(100);
	PEout(3)=0;
	PDout(7)=1;
	PGout(7)=1;
	delay_1ms(100);
	PDout(7)=0;
	PGout(3)=1;
	delay_1ms(100);
	PGout(7)=0;
	PGout(3)=0;
	delay_1ms(100);
	PGout(7)=0;
	delay_1ms(100);
}
void led_off(void)
{
	PGout(7)=1;
	PAout(12)=1;
	PEout(3)=0;
	PDout(7)=0;
	PGout(3)=0;
}
#ifndef _BSP_LED_H
#define _BSP_LED_H

#include "gd32f4xx.h"
#include "systick.h"
#include "systick.h"
#include "sys.h"
#include "bsp_ultrasonsic.h"

#define RCU_LED0 RCU_GPIOE
#define PORT_LED0 GPIOE
#define PIN_LED0 GPIO_PIN_3

#define RCU_LED1 RCU_GPIOD
#define PORT_LED1 GPIOD
#define PIN_LED1 GPIO_PIN_7

#define RCU_LED2 RCU_GPIOG
#define PORT_LED2 GPIOG
#define PIN_LED2 GPIO_PIN_3

#define RCU_LED3 RCU_GPIOA
#define PORT_LED3 GPIOA
#define PIN_LED3 GPIO_PIN_5

#define RCU_LED4 RCU_GPIOA
#define PORT_LED4 GPIOA
#define PIN_LED4 GPIO_PIN_12

#define RCU_LED5 RCU_GPIOG
#define PORT_LED5 GPIOG
#define PIN_LED5 GPIO_PIN_7

void led_gpio_config(unsigned int RCU_LED,unsigned int PORT_LED,unsigned int PIN_LED);
void led_left(void);
void led_right(void);
void led_off(void);
#endif

7.电压检测

电路是利用3个10K的电阻进行分压,然后对第三个电阻的电压进行采集,将采集的显示到OLED屏上。
QQ截图20221220113241.png

#include "bsp_adc.h"
#include "stdio.h"

#define DEBUG   // 打开这个将会使能 adc_get_val下面的打印信息

uint16_t     adcValue;
uint16_t n,m,k=0;
uint16_t V_Value=0;
static void adc_gpio_init(void)
{
    /* enable the clock */
    rcu_periph_clock_enable(ADC_RCU);
    /* configure GPIO port 附加功能需要配置为 GPIO_MODE_ANALOG */ 
    gpio_mode_set(ADC_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE,ADC_PIN);
}
void adc_config(void)
{
    /* enable ADC0 clock */
    rcu_periph_clock_enable(RCU_ADC0);
    /* config ADC clock */
    adc_clock_config(ADC_ADCCK_PCLK2_DIV8);
    /* reset ADC */
    adc_deinit();
    /* configure the ADC  mode */
    adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT); // 所有ADC都工作在独立模式
    
    /* ADC contineous function disable */
    adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, DISABLE); // 关闭连续模式
    /* ADC scan mode disable */
    adc_special_function_config(ADC0, ADC_SCAN_MODE, DISABLE); // 关闭扫描模式
    
    /* ADC data alignment config */
    adc_data_alignment_config(ADC0,ADC_DATAALIGN_RIGHT); // LSB对齐,低位对齐
    
    /* ADC channel length config */
    adc_channel_length_config(ADC0,ADC_REGULAR_CHANNEL,1U); //  ADC规则通道 长度为1

    /* enable ADC interface */
    adc_enable(ADC0);
    /* wait for ADC stability */
    delay_1ms(1);
    /* ADC calibration and reset calibration */
    adc_calibration_enable(ADC0);  // ADC校准
    /* wait for ADC stability */
    delay_1ms(1);
        
    /* adc 引脚初始化 */
    adc_gpio_init();
}
uint16_t adc_channel_sample(uint8_t channel)
{
    /* ADC regular channel config */
    adc_regular_channel_config(ADC0, 0U, channel, ADC_SAMPLETIME_15); // 15个采样周期 
    /* ADC software trigger enable */
    adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL); // ADC软件触发使能

    /* wait the end of conversion flag */
    while(!adc_flag_get(ADC0, ADC_FLAG_EOC));
    /* clear the end of conversion flag */
    adc_flag_clear(ADC0, ADC_FLAG_EOC);
    /* return regular channel sample value */
    return (adc_regular_data_read(ADC0));
}

void adc_get_val(void)
{
	uint32_t adcValue_x=0;
	uint32_t i,j,t;
	uint16_t adc_get[7];
	//adcValue = adc_channel_sample(ADC_CHANNEL_15);  // 采样
	for(i=0;i<7;i++)
	{
		adc_get[i]=adc_channel_sample(ADC_CHANNEL_15);
	}
    for(i=0;i<6;i++)
	{
		for(j=i+1;j<7;j++)
		{
			if(adc_get[j]>adc_get[i])
			{
				t=adc_get[i];
				adc_get[i]=adc_get[j];
				adc_get[j]=t;
			}
		}
	}
	for(i=1;i<6;i++)
	{
		adcValue_x+=adc_get[i];
	}	
	adcValue=adcValue_x/5;
     V_Value = (adcValue*3)/4;
	n=V_Value/100;
	m=(V_Value%100)/10;
	k=(V_Value%10);
#ifdef DEBUG
			
			OLED_ShowString(1,1,"ADC:",3);
			OLED_ShowNum(30,1,n,1,3);
			
			OLED_ShowNum(36,1,m,1,3);
			OLED_ShowString(42,1,".",3);
			OLED_ShowNum(48,1,k,1,3);
			OLED_ShowString(58,1,"%",3);
			printf("adcValue is :%d\r\n ",adcValue);
			printf("adcValue is :%d\r\n ",V_Value);
#endif
    
}
#ifndef _BSP_ADC_H
#define _BSP_ADC_H

#include "gd32f4xx.h"
#include "systick.h"
#include "bsp_usart.h"
#include "bsp_oled.h"

/* PB1 ADC01_IN9*/
#define ADC_RCU    RCU_GPIOB
#define ADC_PORT   GPIOB
#define ADC_PIN    GPIO_PIN_1

extern uint16_t     adcValue;

void adc_config(void);
uint16_t adc_channel_sample(uint8_t channel);
void adc_get_val(void);

#endif 

8.OLED屏

利用I2C来进行通信
QQ截图20221220114136.png

#include "bsp_i2c.h"
void HW_I2cInit(void)
{
	rcu_periph_clock_enable(RCU_OLED_GPIO);//启用外设时钟功能
	rcu_periph_clock_enable(RCU_OLED_I2C);
	
	gpio_af_set(PORT_OLED,I2C_OLED_AF,PIN_OLED_SDA);//端口复用为串口模式
	gpio_af_set(PORT_OLED,I2C_OLED_AF,PIN_OLED_SCL);
	
	gpio_mode_set(PORT_OLED, GPIO_MODE_AF, GPIO_PUPD_PULLUP, PIN_OLED_SDA);//设置GPIO模式
	gpio_output_options_set(PORT_OLED, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, PIN_OLED_SDA);//设置GPIO输出类型和速度
	
	gpio_mode_set(PORT_OLED, GPIO_MODE_AF, GPIO_PUPD_PULLUP, PIN_OLED_SCL);
	gpio_output_options_set(PORT_OLED, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, PIN_OLED_SCL);
	
	i2c_deinit(I2C_OLED);//复位I2C_OLED
	i2c_clock_config(I2C_OLED, 100000, I2C_DTCY_2);//设置波特率
	i2c_mode_addr_config(I2C_OLED, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x78);//设置模式、传输数据长度、主机地址

	i2c_enable (I2C_OLED);//使能外设
	i2c_ack_config(I2C_OLED,I2C_ACK_ENABLE);
}
#ifndef _BSP_I2C_H_
#define _BSP_I2C_H_

#include "gd32f4xx.h"
#include "systick.h"

#define RCU_OLED_GPIO RCU_GPIOB
#define RCU_OLED_I2C  RCU_I2C1

#define PORT_OLED GPIOB
#define PIN_OLED_SDA GPIO_PIN_11
#define PIN_OLED_SCL GPIO_PIN_10
#define I2C_OLED_AF GPIO_AF_4
#define I2C_OLED    I2C1

void HW_I2cInit(void);

#endif
#include "bsp_oled.h"
#include "stdlib.h"
#include "OLED_Font.h"  	
#include "bsp_i2c.h"
//OLED 的显存
//[0]0 1 2 3 ... 127	
//[1]0 1 2 3 ... 127	
//[2]0 1 2 3 ... 127	
//[3]0 1 2 3 ... 127	
//[4]0 1 2 3 ... 127	
//[5]0 1 2 3 ... 127	
//[6]0 1 2 3 ... 127	
//[7]0 1 2 3 ... 127 	

/**********************************************
//Software IIC Start
**********************************************/
void IIC_Start(void)
{
	OLED_SCLK_Set();
	OLED_SDIN_Set();
	OLED_SDIN_Clr();
	OLED_SCLK_Clr();
}
/**********************************************
//Software IIC Stop
**********************************************/
void IIC_Stop(void)
{
	OLED_SCLK_Set();
	OLED_SDIN_Clr();
	OLED_SDIN_Set();
}
/**********************************************
//Software IIC Ack
**********************************************/
void IIC_Wait_Ack(void)
{
	OLED_SCLK_Set() ;
	OLED_SCLK_Clr();
}
/**********************************************
// IIC Write byte
**********************************************/
void Write_IIC_Byte(unsigned char IIC_Byte)
{
	unsigned char i;
	unsigned char m,da;
	da=IIC_Byte;
	OLED_SCLK_Clr();
	for(i=0;i<8;i++)		
	{
		m=da;
		m=m&0x80;
		if(m==0x80)
		{
			OLED_SDIN_Set();
		}
		else 
		OLED_SDIN_Clr();
		da=da<<1;
		OLED_SCLK_Set();
		OLED_SCLK_Clr();
	}
}
/**********************************************
// IIC Write Command
**********************************************/
void Write_IIC_Command(unsigned char IIC_Command)
{
	#if S_I2C
	IIC_Start();
	Write_IIC_Byte(0x78);            //Slave address,SA0=0
	IIC_Wait_Ack();	
	Write_IIC_Byte(0x00);			//write command
	IIC_Wait_Ack();	
	Write_IIC_Byte(IIC_Command); 
	IIC_Wait_Ack();	
	IIC_Stop();
	#else
	while(i2c_flag_get(I2C1, I2C_FLAG_I2CBSY));
	i2c_start_on_bus (I2C1);
	while(!i2c_flag_get(I2C1, I2C_FLAG_SBSEND));//进入主机模式
	i2c_master_addressing(I2C1, 0x78, I2C_TRANSMITTER);
	while(!i2c_flag_get(I2C1, I2C_FLAG_ADDSEND));//判断地址发送出去
	i2c_flag_clear (I2C1, I2C_FLAG_ADDSEND);//清除ADDSEND位
	while(SET != i2c_flag_get(I2C1, I2C_FLAG_TBE));//进入数据发送状态
	i2c_data_transmit (I2C1, 0x00);
	while(!i2c_flag_get(I2C1, I2C_FLAG_BTC));
	i2c_data_transmit (I2C1, IIC_Command);
	while(!i2c_flag_get(I2C1, I2C_FLAG_BTC));
	i2c_stop_on_bus (I2C1);
	while(I2C_CTL0(I2C1)&0x0200);
	#endif
}
/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(unsigned char IIC_Data)
{
	#if S_I2C
	IIC_Start();
	Write_IIC_Byte(0x78);			//Slave address,SA0=0
	IIC_Wait_Ack();	
	Write_IIC_Byte(0x40);			//write data
	IIC_Wait_Ack();	
	Write_IIC_Byte(IIC_Data);
	IIC_Wait_Ack();	
	IIC_Stop();
	#else
	while(i2c_flag_get(I2C1, I2C_FLAG_I2CBSY));
	i2c_start_on_bus (I2C1);
	while(!i2c_flag_get(I2C1, I2C_FLAG_SBSEND));//进入主机模式
	i2c_master_addressing(I2C1, 0x78, I2C_TRANSMITTER);
	while(!i2c_flag_get(I2C1, I2C_FLAG_ADDSEND));//判断地址发送出去
	i2c_flag_clear (I2C1, I2C_FLAG_ADDSEND);//清除ADDSEND位
	while(SET != i2c_flag_get(I2C1, I2C_FLAG_TBE));//进入数据发送状态
	i2c_data_transmit (I2C1, 0x40);
	while(!i2c_flag_get(I2C1, I2C_FLAG_BTC));
	i2c_data_transmit (I2C1, IIC_Data);
	while(!i2c_flag_get(I2C1, I2C_FLAG_BTC));
	i2c_stop_on_bus (I2C1);
	while(I2C_CTL0(I2C1)&0x0200);
	#endif
}
void OLED_WR_Byte(unsigned dat,unsigned cmd)
{
	if(cmd)
	{
		Write_IIC_Data(dat);
	}
	else 
	{
		Write_IIC_Command(dat);	
	}
}
/********************************************
//Fill Picture
********************************************/
void fill_picture(unsigned char fill_Data)
{
	unsigned char m,n;
	for(m=0;m<8;m++)
	{
		OLED_WR_Byte(0xb0+m,0);		//page0-page1
		OLED_WR_Byte(0x00,0);		//low column start address
		OLED_WR_Byte(0x10,0);		//high column start address
		for(n=0;n<128;n++)
		{
			OLED_WR_Byte(fill_Data,1);
		}
	}
}
/**********************************************
//Set Position
//坐标设置
**********************************************/
void OLED_Set_Pos(unsigned char x, unsigned char y) 
{ 	
	OLED_WR_Byte(0xb0+y,OLED_CMD);
	OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
	OLED_WR_Byte((x&0x0f),OLED_CMD); 
}   	  
/**********************************************
//Turn on OLED display  
//开启OLED显示    
**********************************************/
void OLED_Display_On(void)
{
	OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
	OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON
	OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON
}   
/**********************************************
//Turn off OLED display
//关闭OLED显示    
**********************************************/
void OLED_Display_Off(void)
{
	OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
	OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF
	OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF
}	
/**********************************************
//清屏函数,清完屏,整个屏幕是黑色的!  
**********************************************/	   			 
void OLED_Clear(void)  
{  
	u8 i,n;		    
	for(i=0;i<8;i++)  
	{  
		OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)
		OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址
		OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址   
		for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA); 
	} //更新显示
}
/**********************************************
//亮屏函数,整个屏幕点亮!  
**********************************************/	 
void OLED_On(void)  
{  
	u8 i,n;		    
	for(i=0;i<8;i++)  
	{  
		OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)
		OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址
		OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址   
		for(n=0;n<128;n++)OLED_WR_Byte(1,OLED_DATA); 
	} //更新显示
}
/**********************************************
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//chr:要显示的字符		 
//size:选择字体 16/12  
**********************************************/	  
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{      	
	unsigned char c=0,i=0;	
	c=chr-' ';//得到偏移后的值,为什么做偏移可查看 ASCII 表		
	if(x>Max_Column-1){x=0;y=y+2;}
	if(Char_Size ==16)
		{
			OLED_Set_Pos(x,y);	
			for(i=0;i<8;i++)
			OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
			OLED_Set_Pos(x,y+1);
			for(i=0;i<8;i++)
			OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
		}
		else 
		{	
			OLED_Set_Pos(x,y);
			for(i=0;i<6;i++)
			OLED_WR_Byte(F6x8[c][i],OLED_DATA);	
		}
}
/**********************************************
//m^n函数
**********************************************/	  
u32 oled_pow(u8 m,u8 n)
{
	u32 result=1;	 
	while(n--)result*=m;    
	return result;
}	
/**********************************************
//显示2个数字
//x:0~127
//y:0~63 
//num:数值(0~4294967295);
//len :数字的位数
//size:字体大小
**********************************************/	  			  	 		  
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)
{         	
	u8 t,temp;
	u8 enshow=0;						   
	for(t=0;t<len;t++)
	{
		temp=(num/oled_pow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				OLED_ShowChar(x+(size2/2)*t,y,' ',size2);
				continue;
			}
			else 
				enshow=1; 	 
		}
	 	OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2); 
	}
} 
/**********************************************
//显示一个字符号串
**********************************************/
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 Char_Size)
{
	unsigned char j=0;
	while (chr[j]!='\0')
	{		
		OLED_ShowChar(x,y,chr[j],Char_Size);
		x+=8;
		if(x>120){x=0;y+=2;}
		j++;
	}
}
/**********************************************
//显示汉字
**********************************************/
void OLED_ShowCHinese(u8 x,u8 y,u8 no)
{      			    
	u8 t,adder=0;
	OLED_Set_Pos(x,y);	
    for(t=0;t<16;t++)
	{
		OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
		adder+=1;
     }	
	OLED_Set_Pos(x,y+1);	
    for(t=0;t<16;t++)
	{	
		OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
		adder+=1;
	}					
}
/**********************************************
功能描述:显示显示BMP图片
//x0:0~127
//y0:0~63 
128×64起始点坐标(x,y)
**********************************************/
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
{ 	
 unsigned int j=0;
 unsigned char x,y;
  
  if(y1%8==0) y=y1/8;      
  else y=y1/8+1;
	for(y=y0;y<y1;y++)
	{
		OLED_Set_Pos(x0,y);
    	for(x=x0;x<x1;x++)
		{      
	    	OLED_WR_Byte(BMP[j++],OLED_DATA);	    	
		}
	}
} 
/**********************************************
//初始化SSD1306		
**********************************************/	    
void OLED_Init(void)
{ 	
	OLED_WR_Byte(0xAE,OLED_CMD);//关闭显示	
	OLED_WR_Byte(0x40,OLED_CMD);//---set low column address
	OLED_WR_Byte(0xB0,OLED_CMD);//---set high column address
	OLED_WR_Byte(0xC8,OLED_CMD);//-not offset
	
	OLED_WR_Byte(0x81,OLED_CMD);//设置对比度
	OLED_WR_Byte(0xff,OLED_CMD);

	OLED_WR_Byte(0xa1,OLED_CMD);//段重定向设置
	OLED_WR_Byte(0xa6,OLED_CMD);//
	
	OLED_WR_Byte(0xa8,OLED_CMD);//设置驱动路数
	OLED_WR_Byte(0x1f,OLED_CMD);
	
	OLED_WR_Byte(0xd3,OLED_CMD);
	OLED_WR_Byte(0x00,OLED_CMD);
	
	OLED_WR_Byte(0xd5,OLED_CMD);
	OLED_WR_Byte(0xf0,OLED_CMD);
	
	OLED_WR_Byte(0xd9,OLED_CMD);
	OLED_WR_Byte(0x22,OLED_CMD);
	
	OLED_WR_Byte(0xda,OLED_CMD);
	OLED_WR_Byte(0x02,OLED_CMD);
	
	OLED_WR_Byte(0xdb,OLED_CMD);
	OLED_WR_Byte(0x49,OLED_CMD);
	
	OLED_WR_Byte(0x8d,OLED_CMD);
	OLED_WR_Byte(0x14,OLED_CMD);
	
	OLED_WR_Byte(0xaf,OLED_CMD);
	OLED_Clear();
}  
#ifndef __BSP_OLED_H
#define __BSP_OLED_H	

#include "gd32f4xx.h"
#include "systick.h"
#include "bsp_i2c.h"
#include "stdlib.h"

#define OLED_MODE 0
#define SIZE 8
#define XLevelL		0x00
#define XLevelH		0x10
#define Max_Column	128
#define Max_Row		32
#define	Brightness	0xFF 
#define X_WIDTH 	128
#define Y_WIDTH 	32	    						  
//-----------------OLED 软件I2C端口定义----------------  					   
#define OLED_SCLK_Clr() gpio_bit_reset(GPIOA, GPIO_PIN_2)//设置Pin脚为高电平//SCL IIC接口的时钟信号
#define OLED_SCLK_Set() gpio_bit_set(GPIOA, GPIO_PIN_2)

#define OLED_SDIN_Clr() gpio_bit_reset(GPIOA, GPIO_PIN_3)//SCL IIC接口的数据信号
#define OLED_SDIN_Set() gpio_bit_set(GPIOA, GPIO_PIN_3)
 		     
#define OLED_CMD  0	//写命令
#define OLED_DATA 1	//写数据

#define S_I2C 	0//软件IIC或硬件IIC开关

#define u8 unsigned char
#define u32 unsigned int

//OLED控制用函数
void fill_picture(unsigned char fill_Data);
void IIC_Start(void);
void IIC_Stop(void);
void IIC_Wait_Ack(void);
void Write_IIC_Command(unsigned char IIC_Command);
void Write_IIC_Data(unsigned char IIC_Data);
void Write_IIC_Byte(unsigned char IIC_Byte);

void OLED_WR_Byte(unsigned dat,unsigned cmd);  
void OLED_Display_On(void);
void OLED_Display_Off(void);	   							   		    
void OLED_Init(void);
void OLED_Clear(void);
void OLED_DrawPoint(u8 x,u8 y,u8 t);
void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot);
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size);
void OLED_ShowString(u8 x,u8 y, u8 *p,u8 Char_Size);	 
void OLED_Set_Pos(unsigned char x, unsigned char y);
void OLED_ShowCHinese(u8 x,u8 y,u8 no);
void OLED_DrawBMP(unsigned char x0, 
				unsigned char y0,
				unsigned char x1, 
				unsigned char y1,
				unsigned char BMP[]);



#endif  

9.驱动电路

驱动电路利用驱动芯片RZ7889来驱动
QQ截图20221220150331.png
芯片的说明
QQ截图20221220150525.png
因文本文字过长不允许,所以代码看附件。

10.开发板接线

QQ截图20221220152011.png

11.蓝牙软件页面

Screenshot_20221220_152622.jpg
蓝牙设置软件
QQ截图20221220152833.png
蓝牙模块连接ch_340,TXD连接RXD,RXD连接TXD,然后按住蓝牙模块的按键再上电,然后接可以用软件对蓝牙模块进行设置。

总结

验证小车的过程中发现了一些错误,这些错误已在原来的原理图中修改,并且在原来的小车上验证成功。

设计图

未生成预览图,请在编辑器重新保存一次

BOM

暂无BOM

附件

序号文件名称下载次数
1
1663496466.mp4
1
2
HC-T串口助手V1.4(2022.04.01).exe
5
3
com.hc.mixthebluetooth.apk
715
4
HC05指令集.pdf
4
5
超声波测距资料HC-SR04(参考).rar
5
6
video_20221220_154031.mp4
1
7
OLE显示屏专用取模工具_Image2Lcd_32.rar
3
8
0.96OLED显示屏_数据手册.pdf
3
9
智能小车.zip
14
10
mmexport1671537177883.mp4
1
克隆工程
添加到专辑
0
0
分享
侵权投诉

工程成员

评论

全部评论(1)
按时间排序|按热度排序
粉丝0|获赞0
相关工程
暂无相关工程

底部导航