
基于梁山派开发板的蓝牙遥控小车
简介
基于梁山派开发板的蓝牙遥控小车
简介:基于梁山派开发板的蓝牙遥控小车开源协议
:GPL 3.0
描述
该智能小车能够使用超声波测距并在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.电源模块
利用两节18650电池串联,先经过一个稳压二极管进行稳压,然后经过开关,可输出为驱动电机的电压,然后经过降压芯片输出5V的电压,其中还分别并联两个钽电容进行滤波。
2.蜂鸣器模块
当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.按键电路
把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.超声波测距
使用了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模块。
#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.车灯显示
电路设计
代码部分
#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屏上。
#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来进行通信
#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来驱动
芯片的说明
因文本文字过长不允许,所以代码看附件。
10.开发板接线
11.蓝牙软件页面
蓝牙设置软件
蓝牙模块连接ch_340,TXD连接RXD,RXD连接TXD,然后按住蓝牙模块的按键再上电,然后接可以用软件对蓝牙模块进行设置。
总结
验证小车的过程中发现了一些错误,这些错误已在原来的原理图中修改,并且在原来的小车上验证成功。
设计图

BOM


评论