这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » w759067820的智能车DIY进程贴--!更新至小车绕8字!

共19条 1/2 1 2 跳转至

w759067820的智能车DIY进程贴--!更新至小车绕8字!

菜鸟
2015-05-25 12:40:15     打赏

平衡车,好高大上。。一个月前接触STM32,刚好碰到这个活动,做个东西练练手。为以后参加各种电子比赛做准备!第一次做这个东西,发现自己还有很多很多东西需要学习!珍惜这个机会!开干!

发现给的资料都是用寄存器版本写的,程序不方便解读。所以我决定,用库函数再学习一次!

一楼作为快捷跳楼方式,方便查看!点击即可链接。。


1 -------------------------------组装-------------------------

2 ----------------------软件安装与程序烧录--------------- 补充了 蓝牙连接教程!

3 --------------------------学习进程------------------------- 觉得写得不够详细,又重新更了!


          3.1 点亮LED学习。

    3.2 按键学习。

    3.3 OLED显示 学习。

    3.4 AD采集、获得电压值

    3.5 定时器学习

4 ----------------------------电机驱动实验--------------------------------

5 -----------------------编码器数据采集实验 ---------------------------

6 -------------------------------蓝牙控制----------------------------------

7 ------------------------------超声波避障--------------------------------

8 ------------------------------小车绕8字---------------------------------

9 --------------------总结--------------------

学习进行中------

终于有视频咯!


视频地址:http://player.youku.com/player.php/sid/XMTMzNzg4NTQ3Mg==/v.swf
 



菜鸟
2015-05-25 16:32:33     打赏
2楼

一、组装

 !准备好所有的组装材料,开始组装!(悲催的发现少了一个固定OLED的螺丝,应该无关紧要吧)

第一步。将车底架的电机的螺丝拧好,为固定主板做准备。

第二步。紧接着将电池放在上面!PS,发现这硬件设计不是一般好,简直perfect.电池刚刚卡在柱子中!这种电池也是杠杠的!妈妈再也不担心我的速度了!!

第三步。固定好主控制板!

第四步。固定好上亚克力板。

第五步。装上轮胎,插上电源线和电机线。组装完毕!

上电试一试,发现里面卖家已经下载好测试程序。上电后就能看到OLED上面显示各种参数。可以看到工作方式、温度、电机转速、电压、角度值。


保持平衡,按下按键,整个小车就能顺利的工作了,工作时OLED参数就不动态显示额。用手推动小车,小车能恢复平衡,但是用力过猛,会出现翻车现象。拿起来电机猛转,感觉角度度超过某个设定值就电子就不工作了。

最后帖一张小车自平衡的图!

接下来的工作再看看程序。增加新功能。

视频录制之中。。敬请期待!


菜鸟
2015-05-26 11:14:03     打赏
3楼

软件的安装

以前学习51用的keilC51版,然后又学习32,又得再安装一个ARM版本的keil。两个软件有的时候会不兼容,所以觉得特别不方便。后来发现了这个keil5软件。可以同时使用C51和ARM。

此楼附上我的Keil软件。
点击此处下载链接: http://pan.baidu.com/s/1i39vPMP 密码: j7sr

先安装4个安装keil安装和补丁文件,在进行破解。一定要安装keil5pack,不然选择不了芯片!

程序的烧录

使用MCUISP 烧录机进行程序的烧录,

第一步。硬件准备

连接好USB串口下载线

PA10----连接下载器TXD

PA9-----连接下载器RXD

GND连接

将小车拨码开关拨到OFF

第二步。软件的下载按照图中所示。即可给小车下载程序了。

注意:下载程序后,重新上电要将拨码开关拨到ON,小车才能运行

蓝牙连接

第一步,将资料中的APP安装到安卓手机上面

第二步、蓝牙里面先对蓝牙设备进行配对,配对密码为 1234 。紧接着打开APP,按照下图所示步骤连接蓝牙。


成功连接后就可以通过蓝牙来控制小车!


菜鸟
2015-05-27 18:09:03     打赏
4楼

想了想,我还是觉得要从零开始学起,第一步点亮LED灯。库函数走起!!


一、查看LED连接的原理图

注意图示的LED方向错了

再原理图中LED灯连接 PB8 端口,根据图示说明 ,只要将 PB8 IO口的电平拉低,就将点亮LED灯!

二、编写LED端口初始化的程序:在程序中,使用IO口模式为推挽输出。

三、编写LED点亮的子程序;由于使用的位带(bit-band)操作,使得操作STM32的IO口如同51一样方便。大大提高了编程的速度!

支持位带操作的两个内存区的范围是:
              0x2000_0000‐0x200F_FFFF(SRAM 区中的最低 1MB)
              0x4000_0000‐0x400F_FFFF(片上外设区中的最低 1MB)
       对 SRAM 位带区的某个比特,记它所在字节地址为 A,位序号为 n(0<=n<=7),则该比特在别名区的地址为:
              AliasAddr=0x22000000+((A-0x20000000)*8+n)*4=0x22000000+(A-0x20000000)*32+n*4
       对于片上外设位带区的某个比特,记它所在字节的地址为 A,位序号为 n(0<=n<=7),则该比特在别名区的地址为:
              AliasAddr=0x42000000+((A-0x40000000)*8+n)*4=0x42000000+(A-0x40000000)*32+n*4
       上式中,“*4”表示一个字为 4 个字节,“*8”表示一个字节中有 8 个比特。
举个例子 定义PBout端口,那么 

 A = GPIOA_BASE + ODR的偏移地址  = GPIOA_BASE + 0x0c

 A = GPIOA_BASE + IDR的偏移地址   = GPIOA_BASE + 0x08

#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C

#define BitBand(Addr, BitNum)      *((volatile unsigned long *) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))  //乘32等于左移5乘5等于左移2

#define PBout(n) BitBand(GPIOB_ODR_Addr,n)

#define LED1 PBout(8)

具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).  

这样就可以对LED1进行位操作

在程序中是这么实现的:

//位带操作,实现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)  //输入

 

同时宏定义LED端口
#define LED1 PBout(8)

LED点亮的子程序:

 

最后、在主程序中点亮LED

int main(void)
{
    delay_init();
    LED_Init();
    while(1)
    {
        LED_Flash(200); //led闪烁延时200ms
    }
}

这样就顺利的点亮了LED灯!


菜鸟
2015-05-28 15:59:44     打赏
5楼

按键学习:

一、根据原理图设置IO口;由图中可以知道 按键IO口为 PB 5,并且连接有上拉电阻,所以在配置IO口的时候需要配置为上拉输入模式GPIO_Mode_IPU。同时按键输入信号为低电平。

二、进行按键的初始化,在案件的初始化过程中,我们需要知道的是按键所用GPIOB挂在RCC_APB2控制总线上,所以在初始化中,第一步需要使能PB端口的时钟,使用函数为RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  然后再进行IO的配置过程,管脚为PB5,模式使用上拉输入模式GPIO_Mode_IPU;

这里需要注意在时钟初始化 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE),写成RCC_APB1PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE),在编译过程中,是不会报错的!

以下为按键初始化函数:

在主函数中调用KEY-Init();进行初始化!

三、进行按键扫描函数的编写,这里我们可以调用GPIO_ReadInputDataBit()函数来读取IO口的状态;可以在.h文件中宏定义#define KEY0 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5),我们简单的进行按键扫描程序的编写:

void Key_Scan(void)
{
     if(KEY1==0)
    {
        delay_ms(10); //按键消抖
        //这里写按键操作的程序 比如:

        LED1 = ~LED1;
    }
}
这只是我简单的学习程序,就能实现按键的操作!当然在实际实践中,还是要参考DIY例程中的程序,能将一个按键进行多种控制方式!

 


菜鸟
2015-06-05 23:45:18     打赏
6楼

OLED的测试:

先科普一下OLED,即有机发光二极管( Organic Light-Emitting Diode),又称为有机电激光显示( Organic
Electroluminesence Display, OELD)。 OLED 由于同时具备自发光,不需背光源、对比度高、
厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优
异之特性,被认为是下一代的平面显示器新兴应用技术。
LCD 都需要背光,而 OLED 不需要,因为它是自发光的。这样同样的显示, OLED 效果要来得好一些。 以目前的技术, OLED 的尺寸还难以大型化,但是分辨率确可以做到很高。

OLED的接口方式:8080 接口方式
CS: OLED 片选信号。
WR:向 OLED 写入数据。
RD:从 OLED 读取数据。
D[7: 0]: 8 位双向数据线。
RST(RES):硬复位 OLED。
DC:命令/数据标志( 0,读写命令; 1,读写数据)。

4线串行(SPI)接口方式 也是小车使用的口方式

CS: OLED 片选信号。
RST(RES):硬复位 OLED。
DC:命令/数据标志( 0,读写命令; 1,读写数据)。
SCLK:串行时钟线。
SDIN:串行数据线。

根据原理图和小车硬件的设置:原理图标识有错误!

OLED连线为

D/C----------PB9
RST--------PC13

SCLK------PC15

SDIN-------PC14

我觉得对于OLED,我先搞懂怎么用,具体的我暂时不打算深究!我想原理应该和点阵差不多吧。我只了解了接口,然后了解显示函数的用法就行了。以后再研究研究吧!

附上我的ID show!

 


 


菜鸟
2015-06-09 20:05:13     打赏
7楼

最近,学校安排实训!然后周末又要考四级!时间安排不当,这周就不打算更了!我要努力的考四级,都考了3次了,还没过!


菜鸟
2015-09-02 21:26:37     打赏
8楼

经过一个有意义的暑假,然而四级还是没过。参加了电子竞赛,发现自己真的需要好好地学习单片机,还需要更多硬件的知识!今年的比赛选择了B题,被风机坑了。虽然没有取得好的名次,但是参与了,收获还是多多的!今年第一次,没什么经验,自己加油,明年再战!


菜鸟
2015-09-02 21:36:42     打赏
9楼

忙完了比赛,我还是好好继续来研究这个平衡车。上次更新到OLED的学习。有了显示,我们就可以进行更多的操作了。OLED的显示,能方便我们了解更多的内容!比如,采集的电压值,小车编码器的值都能清楚明了的显示在小车上,就不需要再接串口将数据传输到电脑上观察。

 

先来了解了解STM32F103C8T6ADC

STM32 ADC 12 位逐次逼近型的模拟数字转换器。它有18个通道,可测量16个外部和2个内部信号源,ADC_IN16为内部温度采集。各通道的 A/D 转换可以单次、连续、扫描或间断模式执行。 ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。

这个小车所使用的32芯片有2ADCSTM32 ADC 最大的转换速率为 1Mhz,也就是转换时间为 1us(在 ADCCLK=14M,采样周期为1.5 ADC 时钟下得到),不要让 ADC 的时钟超过 14M,否则将导致结果准确度下降。

根据原理图,

我们可以知道,对应的电压值采集的STM32引脚为PA4。所以采用ADC1的通道4

那么在配置ADC的初始化时候,就需要配置GPIOA_4

还是常规要求,在使用32的某个功能时,需要开启某部分的时钟,这里就需要开启ADC时钟,和GPIOA的时钟

第一步:使能GPIOAADC1通道时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1 , ENABLE ); 接下来初始化ADC引脚。

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //输入模拟信号:模式为模拟输入

GPIO_Init(GPIOA,&GPIO_InitStructure);

第二步;复位ADC1,设置分频因子

由于使用的库函数,需要将

stm32f10x_adc.c这个文件添加进工程,

接下来就可以直接调用函数

ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值

RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12, ADC最大时间不能超过14M

第三步,初始化ADC1参数

ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立工作模式

ADC_InitStructure.ADC_ScanConvMode = DISABLE; //单通道模式

ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //单次转换模式

ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //软件而不是外部触发启动

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐

ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目

ADC_Init(ADC1,&ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器

第四步:使能ADC并校准

在完成上面的操作就可以使能和校准ADC。为了得到精准的AD转换结果,都必须要校准ADC。但是要注意的是,每次进行校准之后要等待校准结束。这里是通过获取校准状态来判断是否校准是否结束。

下面是ADC使能和校准的方法:

ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1

ADC_ResetCalibration(ADC1); //使能复位校准

while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束

ADC_StartCalibration(ADC1); //开启AD校准

while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束

第五步:读取AD转换值

上面校准完成之后,就可以设置规则序列 1 里面的通道,采样顺序,以及通道的采样周期,然后启动 ADC 转换。在转换结束后,读取 ADC 转换结果值就是了。

设置规则序列通道以及采样周期的函数:

ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,序列1,采样时间为239.5周期

设置软件开启 ADC 转换的方法是:

ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能

我们要判断 ADC1的转换是否结束。所以需要 进行以下操作:

while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC )); //等待转换结束

最后再获取ADC转换结果

ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果。

这里可以将获取ADC值得程序编写为一个子程序

/*************************************************************************

函数功能:获得ADC

入口参数:ADC1 的通道

返回 值:AD转换结果

*************************************************************************/

u16 Get_Adc(u8 ch)

{

ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );

ADC_SoftwareStartConvCmd(ADC1, ENABLE);

while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));

return ADC_GetConversionValue(ADC1);

}

在实际的程序中,一般都是多次采集AD值,然后取平均值,这样能得到较为准确的值。

所以我编写下面的函数来实现

/*************************************************************************

函数功能:获得多次ADC平均值

入口参数:ADC1 的通道,采样次数

返回 值:AD转换结果

*************************************************************************/

u16 Get_Adc_Average(u8 ch,u8 times)

{

u32 temp_val=0;

u8 t;

for(t=0;t

{

temp_val+=Get_Adc(ch);

delay_ms(5);

}

return temp_val/times;

}

通过这种方式获取ADC的值为0-4096;我们还需要进一步转换,才能得到电压值。由于STM32没有可设置的外部电压,所以使用内部3.3V为参考电压,需要采集的电压值大于3.3v,所以在硬件设计将设置AD采样电阻,查看原理图,可以看到,最终AD采集的电压为R2上面的电压。

下面是读取电压值的子程序

/*************************************************************************

函数功能:读取电池电压

入口参数:无

返回 值:无

*************************************************************************/

void Get_battery_volt(void)

{

Voltage=Get_Adc(4)*3.3*11.5*100/1.5/4096;

}

这里多乘了100是方便小数部分的显示。


菜鸟
2015-09-17 18:56:57     打赏
10楼
更新节点!终于有咯视频!

共19条 1/2 1 2 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]