视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
基于stm32的多功能小车
2025-09-26 21:49:03 责编:小OO
文档
基于stm32的多功能小车

     摘 要

     STM32系列基于专为要求高性能、低成本、低功耗的嵌入式应用专门设计的ARM Cortex-M3内核。本次多功能小车采用stm32f103为主控芯片,用多个模块组合的形式组合来实现功能,本次设计的主要目的是学如何使用stm32,例如IO输入输出模式,时钟的配置,定时器的使用,串口,adc,spi,等等,小车实现了循迹功能,蓝牙,红外遥控,以及无线摇杆控制。

关键词:stm32,蓝牙,无线摇杆,循迹,

一设计任务与要求……………………………………………………..1

二单元电路设计………………………………………………………...2

 1循迹模块………………………………………………3

 2驱动模块………………………………………………3

 3稳压模块………………………………………………4

 4蓝牙模块………………………………………………4

 5红外接收模块…………………………………………5

 6摇杆摇控器原理图……………………………………6

三测试结果………………………………………………………………8

四各模块程序…………………………………………………………....8

五收获与心得…………………………………………………………..34

一设计任务与要求

   1寻迹功能(可以稳定的寻单黑线运行,不冲出赛道)

   2 红外遥控功能(用一个红外遥控器可以控制小车,并且支持遥控器连续按下来控制小车左右转,前进,后退)

   3蓝牙遥控功能(通过一个蓝牙模块充当从机,手机蓝牙充当主机,)

   4基于NRF2401的遥控控制功能

 

二单元电路设计

1循迹模块

  光电循线由红外对管组成,通过检测接收到的反射光强判断黑白线从而,达到循迹的功能.本次采用的4路循迹.

2驱动模块

   电机驱动采用L298N,一片L298N可以驱动两个电机,内部包含4通道逻辑驱动电路,

L298N的逻辑驱动

L298N的逻辑功能:

循迹模块电路图

3稳压模块

 该模块可以提供+5v,+7.8的电压.

4蓝牙模块

  

5红外接收

5摇杆遥控器原理

三测试结果

1实现了红外遥控的选择模式功能:

1键:红外遥控功能,前进,后退,左转,右转功能良好,并且支持遥控连续按

2键:循迹模式,能够循迹简单的赛道,速度可调,但弯度太大时,循迹效果不好,速度需要多次调试。

3键:蓝牙模式,能通过手机控制小车的动作,控制距离比蓝牙远,并且无死角,蓝牙控制稳定。

4无线遥控模式,通过自制的遥控,控制小车,左摇杆控制小车方向,右摇杆控制小车速度,方向控制效果比较好,但是由于电机,控制速度的效果不是非常明显,但基本上实现的速度的控制。

四各模块的程序

1红外遥控

if(text_falg==1)

        {     LED0=!LED0;

            key=Remote_Scan();      

            switch(key)

            {

                case 0:     //str="ERROR";

                {    break;    }           

              case 162:  EN1=0;

                   EN2=0;                 break;     //power

        case 168:  EN1=1;   //ºóÍË

                   EN2=1;

                   PB7=0;

                   PB8=0;

TIM2->CCR3=800;

TIM2->CCR4=800;

                        delay_ms(50);    

                           EN1=0;

                   EN2=0;

                                     break;    

        case 98:   EN1=1;//ǰ½ø

                   EN2=1;

                           PB7=1;

                   PB8=1;

             TIM2->CCR3=100;

TIM2->CCR4=100;

                           delay_ms(50);

                           EN1=0;

                   EN2=0;

                                    break;       

        case 194:  EN1=1;   //zou

                   EN2=0;

                                PB7=1;

                   PB8=1;

TIM2->CCR3=100;

                   delay_ms(50);    

                                     EN1=0;

                                     break;                                                       //str="UP";

                case 34:   EN1=0;  //you

                   EN2=1;

                                PB7=1;

                   PB8=1;

TIM2->CCR4=100;

                   delay_ms(50);    

                                     EN2=0;

                                        break;                                                    

                                

                  }

2红外接收

#include "remote.h"

#include "delay.h"

#include "usart.h"

//红外遥控初始化

//设置IO以及定时器4的输入捕获

void Remote_Init(void)                  

{  

    GPIO_InitTypeDef GPIO_InitStructure;

    NVIC_InitTypeDef NVIC_InitStructure;

    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

    TIM_ICInitTypeDef  TIM_ICInitStructure;  

     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能PORTB时钟 

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);    //TIM5 时钟使能 

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;                 //PA1 输入 

     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;         //上拉输入 

     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

     GPIO_Init(GPIOA, &GPIO_InitStructure);

     GPIO_SetBits(GPIOA,GPIO_Pin_1);    //初始化GPIOA1

    

                          

     TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出  

    TIM_TimeBaseStructure.TIM_Prescaler =(72-1);     //预分频器,1M的计数频率,1us加1.       

    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim

    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式

    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx

  TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;  // 选择输入端 IC2映射到TI5上

  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;    //上升沿捕获

  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;

  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;     //配置输入分频,不分频 

  TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波

  TIM_ICInit(TIM5, &TIM_ICInitStructure);//初始化定时器输入捕获通道

  TIM_Cmd(TIM5,ENABLE );     //使能定时器5

 

                         

    NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;  //TIM5中断

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级0级

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能

    NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器    

 

     TIM_ITConfig( TIM5,TIM_IT_Update|TIM_IT_CC2,ENABLE);//允许更新中断 ,允许CC2IE捕获中断    

     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOA, ENABLE);     //使能PB,PE端口时钟

// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);

    

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //LED0-->PB.5 端口配置

 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;          //推挽输出

 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;         //IO口速度为50MHz

 GPIO_Init(GPIOB, &GPIO_InitStructure);                     //根据设定参数初始化GPIOB.5

 GPIO_SetBits(GPIOB,GPIO_Pin_0);                         //PB.5 输出高

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;             //LED1-->PE.5 端口配置, 推挽输出

 GPIO_Init(GPIOB, &GPIO_InitStructure);                       //推挽输出 ,IO口速度为50MHz

 GPIO_SetBits(GPIOB,GPIO_Pin_5);                          //PE.5 输出高 

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;             //LED1-->PE.5 端口配置, 推挽输出

 GPIO_Init(GPIOB, &GPIO_InitStructure);                       //推挽输出 ,IO口速度为50MHz

 GPIO_SetBits(GPIOB,GPIO_Pin_6);                          //PE.5 输出高     

     

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;             //LED1-->PE.5 端口配置, 推挽输出

GPIO_Init(GPIOB, &GPIO_InitStructure);                       //推挽输出 ,IO口速度为50MHz

GPIO_SetBits(GPIOB,GPIO_Pin_7);                 

    

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;             //LED1-->PE.5 端口配置, 推挽输出

GPIO_Init(GPIOB, &GPIO_InitStructure);                       //推挽输出 ,IO口速度为50MHz

GPIO_SetBits(GPIOB,GPIO_Pin_8);     

GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_9;//PA0

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //PA0设置成输入,默认下拉      

GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOA.0

GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_10;//PA0

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //PA0设置成输入,默认下拉      

GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOA.0

GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_11;//PA0

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //PA0设置成输入,默认下拉      

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;         //IO口速度为50MHz

//GPIO_SetBits(GPIOA,GPIO_Pin_7);                         //PB.5 输出高

GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOA.0

GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_12;//PA0

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //PA0设置成输入,默认下拉      

GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOA.0

 

}

 

//遥控器接收状态

//[7]:收到了引导码标志

//[6]:得到了一个按键的所有信息

//[5]:保留    

//[4]:标记上升沿是否已经被捕获                                   

//[3:0]:溢出计时器

u8     RmtSta=0;            

u16 Dval;        //下降沿时计数器的值

u32 RmtRec=0;    //红外接收到的数据                   

u8  RmtCnt=0;    //按键按下的次数      

//定时器5中断服务程序     

void TIM5_IRQHandler(void)

{                  

    if(TIM_GetITStatus(TIM5,TIM_IT_Update)!=RESET)

    {

        if(RmtSta&0x80)//上次有数据被接收到了

        {    

            RmtSta&=~0X10;                        //取消上升沿已经被捕获标记

         if((RmtSta&0X0F)==0X00)RmtSta|=1<<6;//标记已经完成一次按键的键值信息采集

         if((RmtSta&0X0F)<14)RmtSta++;

            else

            {

             RmtSta&=~(1<<7);//清空引导标识

                RmtSta&=0XF0;    //清空计数器    

            }                                    

        }                                

    }

     if(TIM_GetITStatus(TIM5,TIM_IT_CC2)!=RESET)

    {      

        if(RDATA)//上升沿捕获

        {

            TIM_OC2PolarityConfig(TIM5,TIM_ICPolarity_Falling);        //CC1P=1 设置为下降沿捕获                

            TIM_SetCounter(TIM5,0);           //清空定时器值

            RmtSta|=0X10;                    //标记上升沿已经被捕获

        }else //下降沿捕获

        {            

               Dval=TIM_GetCapture2(TIM5);//读取CCR1也可以清CC1IF标志位

             TIM_OC2PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC4P=0    设置为上升沿捕获

             

            if(RmtSta&0X10)                    //完成一次高电平捕获 

            {

                 if(RmtSta&0X80)//接收到了引导码

                {

                    

                 if(Dval>300&&Dval<800)            //560为标准值,560us

                    {

                     RmtRec<<=1;    //左移一位.

                        RmtRec|=0;    //接收到0       

                 }else if(Dval>1400&&Dval<1800)    //1680为标准值,1680us

                    {

                     RmtRec<<=1;    //左移一位.

                        RmtRec|=1;    //接收到1

                 }else if(Dval>2200&&Dval<2600)    //得到按键键值增加的信息 2500为标准值2.5ms

                    {

                        RmtCnt++;         //按键次数增加1次

                        RmtSta&=0XF0;    //清空计时器        

                    }

              }else if(Dval>4200&&Dval<4700)        //4500为标准值4.5ms

                {

                 RmtSta|=1<<7;    //标记成功接收到了引导码

                    RmtCnt=0;        //清除按键次数计数器

                }                         

            }

         RmtSta&=~(1<<4);

        }                                                             

    }

 TIM_ClearFlag(TIM5,TIM_IT_Update|TIM_IT_CC2);        

}

//处理红外键盘

//返回值:

//     0,没有任何按键按下

//其他,按下的按键键值.

u8 Remote_Scan(void)

{        

    u8 sta=0;       

    u8 t1,t2;  

if(RmtSta&(1<<6))//得到一个按键的所有信息了

    { 

t1=RmtRec>>24;            //得到地址码

t2=(RmtRec>>16)&0xff;    //得到地址反码 

         if((t1==(u8)~t2)&&t1==REMOTE_ID)//检验遥控识别码(ID)及地址 

        { 

t1=RmtRec>>8;

            t2=RmtRec;     

            if(t1==(u8)~t2)sta=t1;//键值正确     

        }   

        if((sta==0)||((RmtSta&0X80)==0))//按键数据错误/遥控已经没有按下了

        {

          RmtSta&=~(1<<6);//清除接收到有效按键标识

            RmtCnt=0;        //清除按键次数计数器

        }

    }  

    return sta;

}

3循迹程序

  if(  text_falg==2)

            {   LED1=0; 

             if( a==0&&b!=0&&(c!=0||d!=0) )

                   { EN1=1;//前进

                   EN2=0;

                           PB7=1;

                   PB8=1;

             TIM2->CCR3=300;

                                     delay_us(100);

// TIM2->CCR4=200;

                                   

                                                 }

                                                                 

                                         if( b==0&&c!=0&&(a!=0||b!=0) )

                   {  

                                   EN1=1;//前进

                   EN2=1;

                           PB7=1;

                   PB8=1;

             TIM2->CCR3=450;

TIM2->CCR4=350;

                                    delay_us(100);             }

                                

                              if( c==0&&d!=0&&(a!=0||b!=0) )

                   {  

                                   EN1=1;//前进

                   EN2=1;

                           PB7=1;

                   PB8=1;

             TIM2->CCR3=350;

TIM2->CCR4=450;

                                      delay_us(100);           }

                                 

                           if(d==0&&c!=0&&(a!=0||b!=0))

                            {

                                   EN1=0;//前进

                   EN2=1;

                           PB7=1;

                   PB8=1;

             TIM2->CCR3=250;

TIM2->CCR4=350;

                                delay_us(100);

               }

            

                if(a!=0&&b!=0&&c!=0&&d!=0)   

                                 {

                                     EN1=1;//前进

                   EN2=1;

                           PB7=1;

                   PB8=1;

             TIM2->CCR3=350;

TIM2->CCR4=350;

                                     delay_us(100);

                       }

                            

                      }

4蓝牙串口判断

if( text_falg==3)                                                  

    

    LED0=0;

    

  //if((USART_RX_STA&0x8000)==0)

    //     {  LED1=0;

         switch(USART_RX_BUF[0])

          { case 'A':LED0=1;

                             EN1=1;

                   EN2=1;

                           PB7=1;

                   PB8=1;

             TIM2->CCR3=100;

TIM2->CCR4=100;                 

                               break; 

                      case 'B':

                             EN1=1;  //you

                   EN2=1;

                                PB7=1;

                   PB8=1;  

TIM2->CCR3=100;

TIM2->CCR4=400;                            

                               break; 

                      case 'C':

                             EN1=1;   //zou

                   EN2=1;

                                PB7=1;

                   PB8=1;

                     TIM2->CCR3=400;

TIM2->CCR4=100;    

                               break; 

                       case 'D':

                             EN1=1;   //后退

                   EN2=1;

                   PB7=0;

                   PB8=0;

TIM2->CCR3=800;

TIM2->CCR4=800;             

                               break; 

                      case 'S':

                              EN1=0;   

                    EN2=0;                 

                              break; 

                            

                     

                            }                 

             USART_RX_STA=0;

    //        }

         }    

5蓝牙串口初始化

#include "sys.h"

#include "usart.h"      

//如果使用ucos,则包括下面的头文件即可.

#if SYSTEM_SUPPORT_UCOS

#include "includes.h"                    //ucos 使用      

#endif

#if 1

#pragma import(__use_no_semihosting)             

//标准库需要的支持函数                 

struct __FILE 

    int handle; 

}; 

FILE __stdout;       

//定义_sys_exit()以避免使用半主机模式    

_sys_exit(int x) 

    x = x; 

//重定义fputc函数 

int fputc(int ch, FILE *f)

{      

while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   

USART1->DR = (u8) ch;

    return ch;

}

#endif 

/*使用microLib的方法*/

 /* 

int fputc(int ch, FILE *f)

{

    USART_SendData(USART1, (uint8_t) ch);

    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}    

   

    return ch;

}

int GetKey (void)  { 

while (!(USART1->SR & USART_FLAG_RXNE));

return ((int)(USART1->DR & 0x1FF));

}

*/

 

//串口1中断服务程序

//注意,读取USARTx->SR能避免莫名其妙的错误       

u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.

//接收状态

//bit15,    接收完成标志

//bit14,    接收到0x0d

//bit13~0,    接收到的有效字节数目

u16 USART_RX_STA=0;       //接收状态标记      

//初始化IO 串口1 

//bound:波特率

void uart_init(u32 bound){

    //GPIO端口设置

    GPIO_InitTypeDef GPIO_InitStructure;

    USART_InitTypeDef USART_InitStructure;

    NVIC_InitTypeDef NVIC_InitStructure;

     

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);    //使能USART1,GPIOA时钟

     USART_DeInit(USART1);  //复位串口1

     //USART1_TX   PA.9

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    //复用推挽输出

    GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9

   

    //USART1_RX      PA.10

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入

    GPIO_Init(GPIOA, &GPIO_InitStructure);  //初始化PA10

   //Usart1 NVIC 配置

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;        //子优先级3

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能

    NVIC_Init(&NVIC_InitStructure);    //根据指定的参数初始化VIC寄存器

  

   //USART 初始化设置

    USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;

    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式

    USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位

    USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位

    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制

    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;    //收发模式

    USART_Init(USART1, &USART_InitStructure); //初始化串口

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断

    USART_Cmd(USART1, ENABLE);                    //使能串口 

}

#if EN_USART1_RX   //如果使能了接收

void USART1_IRQHandler(void)                    //串口1中断服务程序

    {

    u8 Res;

#ifdef OS_TICKS_PER_SEC         //如果时钟节拍数定义了,说明要使用ucosII了.

    OSIntEnter();    

#endif

    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)

        {

     Res =USART_ReceiveData(USART1);//(USART1->DR);    //读取接收到的数据

        

        if((USART_RX_STA&0x8000)==0)//接收未完成

            {

            if(USART_RX_STA&0x4000)//接收到了0x0d

                {

                if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始

                else USART_RX_STA|=0x8000;    //接收完成了 

                }

            else //还没收到0X0D

                {    

                if(Res==0x0d)USART_RX_STA|=0x4000;

                else

                    {

                    USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;

                    USART_RX_STA++;

                 if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收      

                    }         

                }

            }            

     } 

#ifdef OS_TICKS_PER_SEC         //如果时钟节拍数定义了,说明要使用ucosII了.

    OSIntExit();                                               

#endif

#endif    

6遥控(2401)接收程序

#include "24l01.h"

#include "delay.h"

//#include "spi.h"

//#include "stm32f10x.h"

//#include "stm32f10x_spi.h"

    

const u8 TX_ADDRESS[TX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //发送地址

const u8 RX_ADDRESS[RX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //发送地址

//初始化24L01的IO口

void NRF24L01_Init(void)

{  

    GPIO_InitTypeDef GPIO_InitStructure;

    SPI_InitTypeDef  SPI_InitStructure; 

    

     RCC_APB2PeriphClockCmd(    RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOB, ENABLE );    

    

    //GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;

    GPIO_InitStructure.GPIO_Pin =GPIO_Pin_4;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOC, &GPIO_InitStructure); 

    GPIO_SetBits(GPIOC,GPIO_Pin_4);

    

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU  ;   //上拉输入

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    

     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //LED0-->PB.5 端口配置

 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;          //推挽输出

 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;         //IO口速度为50MHz

 GPIO_Init(GPIOB, &GPIO_InitStructure);                     //根据设定参数初始化GPIOB.5

 GPIO_SetBits(GPIOB,GPIO_Pin_0);                         //PB.5 输出高

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;             //LED1-->PE.5 端口配置, 推挽输出

 GPIO_Init(GPIOB, &GPIO_InitStructure);                       //推挽输出 ,IO口速度为50MHz

 GPIO_SetBits(GPIOB,GPIO_Pin_5);                          //PE.5 输出高 

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;             //LED1-->PE.5 端口配置, 推挽输出

 GPIO_Init(GPIOB, &GPIO_InitStructure);                       //推挽输出 ,IO口速度为50MHz

 GPIO_SetBits(GPIOB,GPIO_Pin_6);                          //PE.5 输出高     

     

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;             //LED1-->PE.5 端口配置, 推挽输出

GPIO_Init(GPIOB, &GPIO_InitStructure);                       //推挽输出 ,IO口速度为50MHz

GPIO_SetBits(GPIOB,GPIO_Pin_7);                 

    

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;             //LED1-->PE.5 端口配置, 推挽输出

GPIO_Init(GPIOB, &GPIO_InitStructure);                       //推挽输出 ,IO口速度为50MHz

GPIO_SetBits(GPIOB,GPIO_Pin_8);     

GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_9;//PA0

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //PA0设置成输入,默认下拉      

GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOA.0

GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_10;//PA0

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //PA0设置成输入,默认下拉      

GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOA.0

GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_11;//PA0

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //PA0设置成输入,默认下拉      

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;         //IO口速度为50MHz

//GPIO_SetBits(GPIOA,GPIO_Pin_7);                         //PB.5 输出高

GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOA.0

GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_12;//PA0

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //PA0设置成输入,默认下拉      

GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOA.0

    

    GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4);

    SPI1_Init();            //初始化SPI

        

    SPI_Cmd(SPI1, DISABLE); // 

    

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工

    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;        //设置SPI工作模式:设置为主SPI

    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;        //设置SPI的数据大小:SPI发送接收8位帧结构

    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;        //选择了串行时钟的稳态:时钟悬空低电平

    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;    //数据捕获于第一个时钟沿

    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;        //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制

    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;        //定义波特率预分频的值:波特率预分频值为256

    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;    //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始

    SPI_InitStructure.SPI_CRCPolynomial = 7;    //CRC值计算的多项式

    SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器

    

    NRF24L01_CE=0;     //使能24L01

    NRF24L01_CSN=1;    //SPI片选取消         

}

//检测24L01是否存在

//返回值:0,成功;1,失败    

u8 NRF24L01_Check(void)

{

    u8 buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};

    u8 i;

    SPI1_SetSpeed(SPI_BaudRatePrescaler_8); //spi速度为9Mhz(24L01的最大SPI时钟为10Mhz)        

    NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,buf,5);//写入5个字节的地址.    

    NRF24L01_Read_Buf(TX_ADDR,buf,5); //读出写入的地址  

for(i=0;i<5;i++)if(buf[i]!=0XA5)break;                                    

    if(i!=5)return 1;//检测24L01错误    

    return 0;         //检测到24L01

}          

//SPI写寄存器

//reg:指定寄存器地址

//value:写入的值

u8 NRF24L01_Write_Reg(u8 reg,u8 value)

{

    u8 status;    

       NRF24L01_CSN=0;                 //使能SPI传输

      status =SPI1_ReadWriteByte(reg);//发送寄存器号 

      SPI1_ReadWriteByte(value);      //写入寄存器的值

      NRF24L01_CSN=1;                 //禁止SPI传输       

      return(status);                   //返回状态值

}

//读取SPI寄存器值

//reg:要读的寄存器

u8 NRF24L01_Read_Reg(u8 reg)

{

    u8 reg_val;        

     NRF24L01_CSN = 0;          //使能SPI传输        

      SPI1_ReadWriteByte(reg);   //发送寄存器号

      reg_val=SPI1_ReadWriteByte(0XFF);//读取寄存器内容

      NRF24L01_CSN = 1;          //禁止SPI传输            

      return(reg_val);           //返回状态值

}    

//在指定位置读出指定长度的数据

//reg:寄存器(位置)

//*pBuf:数据指针

//len:数据长度

//返回值,此次读到的状态寄存器值 

u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len)

{

    u8 status,u8_ctr;           

      NRF24L01_CSN = 0;           //使能SPI传输

      status=SPI1_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值          

  for(u8_ctr=0;u8_ctr      NRF24L01_CSN=1;       //关闭SPI传输

      return status;        //返回读到的状态值

}

//在指定位置写指定长度的数据

//reg:寄存器(位置)

//*pBuf:数据指针

//len:数据长度

//返回值,此次读到的状态寄存器值

u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len)

{

    u8 status,u8_ctr;        

     NRF24L01_CSN = 0;          //使能SPI传输

      status = SPI1_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值

   for(u8_ctr=0; u8_ctr      NRF24L01_CSN = 1;       //关闭SPI传输

      return status;          //返回读到的状态值

}                   

//启动NRF24L01发送一次数据

//txbuf:待发送数据首地址

//返回值:发送完成状况

u8 NRF24L01_TxPacket(u8 *txbuf)

{

    u8 sta;

     SPI1_SetSpeed(SPI_BaudRatePrescaler_8);//spi速度为9Mhz(24L01的最大SPI时钟为10Mhz)   

    NRF24L01_CE=0;

      NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF  32个字节

     NRF24L01_CE=1;//启动发送       

    while(NRF24L01_IRQ!=0);//等待发送完成

    sta=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值       

    NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志

    if(sta&MAX_TX)//达到最大重发次数

    {

        NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器 

        return MAX_TX; 

    }

    if(sta&TX_OK)//发送完成

    {

        return TX_OK;

    }

    return 0xff;//其他原因发送失败

}

//启动NRF24L01发送一次数据

//txbuf:待发送数据首地址

//返回值:0,接收完成;其他,错误代码

u8 NRF24L01_RxPacket(u8 *rxbuf)

{

    u8 sta;                                           

    SPI1_SetSpeed(SPI_BaudRatePrescaler_8); //spi速度为9Mhz(24L01的最大SPI时钟为10Mhz)   

    sta=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值         

    NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志

    if(sta&RX_OK)//接收到数据

    {

        NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据

        NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器 

        return 0; 

    }       

    return 1;//没收到任何数据

}                        

//该函数初始化NRF24L01到RX模式

//设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR

//当CE变高后,即进入RX模式,并可以接收数据了           

void NRF24L01_RX_Mode(void)

{

    NRF24L01_CE=0;      

      NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);//写RX节点地址

      

      NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01);        //使能通道0的自动应答    

      NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01);    //使能通道0的接收地址       

      NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40);            //设置RF通信频率          

      NRF24L01_Write_Reg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度         

      NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);    //设置TX发射参数,0db增益,2Mbps,低噪声增益开启   

      NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG, 0x0f);        //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式 

      NRF24L01_CE = 1; //CE为高,进入接收模式 

}                         

//该函数初始化NRF24L01到TX模式

//设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,选择RF频道,波特率和LNA HCURR

//PWR_UP,CRC使能

//当CE变高后,即进入RX模式,并可以接收数据了           

//CE为高大于10us,则启动发送.     

void NRF24L01_TX_Mode(void)

{                                                         

    NRF24L01_CE=0;        

      NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址 

      NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK      

      NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01);     //使能通道0的自动应答    

      NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址  

      NRF24L01_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次

      NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40);       //设置RF通道为40

      NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);  //设置TX发射参数,0db增益,2Mbps,低噪声增益开启   

      NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG,0x0e);    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断

    NRF24L01_CE=1;//CE为高,10us后启动发送

}

7遥控判断程序

  if(  text_falg==4) 

                    {

                        TIM2_PWM_Init();

                        LED1=!LED1;

                        NRF24L01_Init();

                        while(NRF24L01_Check())

                 {delay_ms(200);}

             NRF24L01_RX_Mode();    

            while(text_falg==4)

                {         

               q=tmp_buf[12]-48;

                 w=tmp_buf[13]-48;

                 s=tmp_buf[14]-48;

                 g=tmp_buf[15]-48;

                         TIM2->CCR3=(q*1000+w*100+10*s)/4;

                     TIM2->CCR4=(q*1000+w*100+10*s)/4;    

                            

                   if(NRF24L01_RxPacket(tmp_buf)==0)//一旦接收到信息,则显示出来.

                   { tmp_buf[32]=0;//加入字符串结束符

                  if(tmp_buf[0]=='4')

                        {  LED0=0;

                                     EN1=1;

                   EN2=1;

                           PB7=1;

                   PB8=1;

                          

                           

                         }

                                                                                             

                    if(tmp_buf[0]=='0')

                          {LED0=1;

                                     EN1=1;

                                     EN2=1;

                                     PB7=0;

                                     PB8=0;

                                    

                        }

                                                

                    if(tmp_buf[6]=='0')

                           {EN1=1;

                                      EN2=0;

                                      PB7=1;

                                      PB8=1; 

                                            }    

                                                                                             

                      if(tmp_buf[6]=='4')

                          {EN1=0;

                                     EN2=1;

                                     PB7=1;

                                     PB8=1; 

                                           }

                                                                                             

                      if(tmp_buf[0]=='2'&&tmp_buf[6]=='2')                                                                         

                               {EN1=0;

                                        EN2=0;

                                          }                            

                                           }      

                                        }

8pwm输出程序

static void TIM2_GPIO_Config(void) 

{

  GPIO_InitTypeDef GPIO_InitStructure;

    /* 设置TIM3CLK 为 72MHZ */

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 

  /* GPIOA and GPIOB clock enable */

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); 

  /*GPIOA Configuration: TIM3 channel 1 and 2 as alternate function push-pull */

  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_2 | GPIO_Pin_3;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;            // 复用推挽输出

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOA, &GPIO_InitStructure);

  /*GPIOB Configuration: TIM3 channel 3 and 4 as alternate function push-pull */

//  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0 | GPIO_Pin_1;

//  GPIO_Init(GPIOB, &GPIO_InitStructure);

}

/**

  * @brief  配置TIM3输出的PWM信号的模式,如周期、极性、占空比

  * @param  无

  * @retval 无

  */

/*

* TIMxCLK/CK_PSC --> TIMxCNT --> TIMx_ARR --> TIMxCNT 重新计数

 *                    TIMx_CCR(电平发生变化)

 * 信号周期=(TIMx_ARR +1 ) * 时钟周期

 * 占空比=TIMx_CCR/(TIMx_ARR +1)

 */

static void TIM2_Mode_Config(void)

{

    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

    TIM_OCInitTypeDef  TIM_OCInitStructure;

    /* PWM信号电平跳变值 */

//     u16 CCR1_Val = 500;        

//     u16 CCR2_Val = 375;

     u16 CCR3_Val = 250;

     u16 CCR4_Val = 125;

/* ----------------------------------------------------------------------- 

    TIM3 Channel1 duty cycle = (TIM3_CCR1/ TIM3_ARR+1)* 100% = 50%

    TIM3 Channel2 duty cycle = (TIM3_CCR2/ TIM3_ARR+1)* 100% = 37.5%

    TIM3 Channel3 duty cycle = (TIM3_CCR3/ TIM3_ARR+1)* 100% = 25%

    TIM3 Channel4 duty cycle = (TIM3_CCR4/ TIM3_ARR+1)* 100% = 12.5%

  ----------------------------------------------------------------------- */

  /* Time base configuration */         

  TIM_TimeBaseStructure.TIM_Period = 999;       //当定时器从0计数到999,即为1000次,为一个定时周期

  TIM_TimeBaseStructure.TIM_Prescaler = 0;        //设置预分频:不预分频,即为72MHz

  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;    //设置时钟分频系数:不分频(这里用不到)

  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数模式

  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  /* PWM1 Mode configuration: Channel1 */

  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;        //配置为PWM模式1

  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;    

//  TIM_OCInitStructure.TIM_Pulse = CCR1_Val;       //设置跳变值,当计数器计数到这个值时,电平发生跳变

//  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  //当定时器计数值小于CCR1_Val时为高电平

//  TIM_OC1Init(TIM2, &TIM_OCInitStructure);     //使能通道1

//  TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);

  /* PWM1 Mode configuration: Channel2 */

//  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

//  TIM_OCInitStructure.TIM_Pulse = CCR2_Val;      //设置通道2的电平跳变值,输出另外一个占空比的PWM

//  TIM_OC2Init(TIM2, &TIM_OCInitStructure);      //使能通道2

//  TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);

  /* PWM1 Mode configuration: Channel3 */

  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

  TIM_OCInitStructure.TIM_Pulse = CCR3_Val;    //设置通道3的电平跳变值,输出另外一个占空比的PWM

  TIM_OC3Init(TIM2, &TIM_OCInitStructure);     //使能通道3

  TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);

  /* PWM1 Mode configuration: Channel4 */

  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

  TIM_OCInitStructure.TIM_Pulse = CCR4_Val;    //设置通道4的电平跳变值,输出另外一个占空比的PWM

  TIM_OC4Init(TIM2, &TIM_OCInitStructure);    //使能通道4

  TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);

  TIM_ARRPreloadConfig(TIM2, ENABLE);             // 使能TIM3重载寄存器ARR

  /* TIM3 enable counter */

  TIM_Cmd(TIM2, ENABLE);                   //使能定时器3    

}

/**

  * @brief  TIM3 输出PWM信号初始化,只要调用这个函数

  *         TIM3的四个通道就会有PWM信号输出

  * @param  无

  * @retval 无

  */

void TIM2_PWM_Init(void)

{

    TIM2_GPIO_Config();

    TIM2_Mode_Config();    

}

五收获与心得

 通过本次基于stm32多功能的小车设计与制作,使我基本上懂得了stm32的基本功能,其中包括IO的输入输出,AD/DA,多路pwm 的产生,定时器,串口等功能。进一步提高我的程序组织能力,在本次制作的过程中,还让我提高了我的硬件制作能力,包括的pcb图的绘制,以及pcb的制作。通过本次多功能小车制作,让我的能力得到全面的提升。下载本文

显示全文
专题