宏定义可以提高编程效率、让代码更简洁,并减少重复代码的写作。
基础的宏定义
我们写代码的时候经常用到绝对值、比较、限幅等,现在将整理如下,你也可以自己建一对.C.H 文件,存放自己常用的一些宏定义。
//====================================================宏定义函数区====================================================
//-------------------------------------------------------------------------------------------------------------------
// @brief 绝对值函数 数据范围是 [-32767,32767]
// @param dat 需要求绝对值的数
// @return int 返回绝对值
// Sample usage: dat = myabs(dat); // 将dat变成正数
//-------------------------------------------------------------------------------------------------------------------
#define myabs(x) ((x)>=0? (x): (-(x)))
//-------------------------------------------------------------------------------------------------------------------
// @brief 限幅 数据范围是 [-32768,32767]
// @param x 被限幅的数据
// @param y 限幅范围(数据会被限制在-y至+y之间)
// @return int 限幅之后的数据
// Sample usage: int dat = limit(500,300); // 数据被限制在-300至+300之间 因此返回的结果是300
//-------------------------------------------------------------------------------------------------------------------
#define limit(x,y) ((x)>(y)? (y): ((x)<-(y)? (-(y)): (x)))
//-------------------------------------------------------------------------------------------------------------------
// @brief 双边限幅 数据范围是 [-32768,32767]
// @param x 被限幅的数据
// @param a 限幅范围左边界
// @param b 限幅范围右边界
// @return int 限幅之后的数据
// Sample usage: int dat = limit_ab(500,-300,400); //数据被限制在-300至+400之间 因此返回的结果是400
//-------------------------------------------------------------------------------------------------------------------
#define limit_ab(x,a,b) (((x)<(a))?(a):(((x)>(b))?(b):(x)))
//-------------------------------------------------------------------------------------------------------------------
// @brief 取两个数最大值最小值 数据范围是 [-32768,32767]
// @param a 数据a
// @param b 数据b
// @return int 限幅之后的数据
// Sample usage: int dat = limit_ab(500,-300,400); //数据被限制在-300至+400之间 因此返回的结果是400
//-------------------------------------------------------------------------------------------------------------------
#define max_ab(a,b) (((a)>=(b))? (a):(b))
#define min_ab(a,b) (((a)>=(b))? (b):(a))
//====================================================宏定义函数区====================================================
蜂鸣器
//===========================================BEEP==========================================//
#define BEEP_PIN_ZF P4_4
#define BEEP_PIN P44
#define BEEP_ON BEEP_PIN = 1
#define BEEP_OFF BEEP_PIN = 0
//BEEP
typedef struct
{
uint16 State;
uint16 Num;
uint16 Time;
uint32 Cnt;
}BeepTypedef;
extern BeepTypedef Beep;
void BeepTick(uint8 n, uint16 time);
void Beep_IntCnt(void);
//===========================================BEEP==========================================//
BeepTypedef Beep;
/**
*@Name :BeepTick
*@Description :BeepTick,中断里计时
*@Param :n:响几次;time:间隔
*@Return :None
*@Sample :BeepTick(2, 100);
**/
void BeepTick(uint8 n, uint16 time)
{
if(Beep.State == 'F')
{
Beep.State = 'T';
Beep.Time = time;
Beep.Num = n;
if(Beep.Num == 0) Beep.Num = 1;
Beep.Cnt = 1;
BEEP_OFF;//gpio_low(BEEP_PIN);
}
}
/**
*@Name :Beep_IntCnt
*@Description :Beep_IntCnt蜂鸣器中断
*@Param :
*@Return :
*@Sample :
**/
void Beep_IntCnt(void)
{
if(Beep.State == 'T')
{
Beep.Cnt ++;
if(Beep.Cnt %Beep.Time == 0) BEEP_PIN = !BEEP_PIN;//gpio_toggle_level(BEEP_PIN);
if((Beep.Cnt %Beep.Time == 0) && ((Beep.Cnt / Beep.Time) == (Beep.Num*2)))
Beep.State = 'F';
}
}
/**
*@Name :BeepParams_Init
*@Description :BeepParams_Init
*@Param :
*@Return :
*@Sample :
**/
void BeepParams_Init(void)
{
Beep.State = 'F';
Beep.Cnt = 0;
Beep.Num = 0;
Beep.Time = 0;
}
按键
//===========================KEY================================//
typedef enum
{
nopress = 0,
onepress,//短按一下
holdpress,//一直按着
} KeyStateEnum;
extern KeyStateEnum KeyUp, KeyDown, KeyLeft, KeyRight, KeyCenter, KeyNext, KeyLast;
#define KEY_UP P70
#define KEY_DOWN P41
#define KEY_LEFT P36
#define KEY_RIGHT P37
#define KEY_CENTER P72
#define KEY_NEXT P66
#define KEY_LAST P46
void KeyScanInt(void);
很好看懂,按键有三种状态,没按下、点按、长按,根据自己按键的数目进行修改就行。
//===========================================KEY======================================//
KeyStateEnum KeyUp, KeyDown, KeyLeft, KeyRight, KeyCenter, KeyNext, KeyLast;
/**
*@Name :KeyScan
*@Description :KeyScan//在中断里扫描
*@Param :None
*@Return :None
*@Sample :KeyScan();
**/
void KeyScanInt(void)
{
static uint8 KeyPressNum = 0;
static uint16 KeyPressTime = 0;
static uint8 SomeKeyPress_Flag = 0;//0 松开 1 有按下 2:消抖后 3:长按
#define AlwaysPressTime 1600//一直按检测时间
#define debouncing 5
if(SomeKeyPress_Flag == 0 && (KEY_UP == 0 || KEY_DOWN == 0 || KEY_CENTER == 0 ||\
KEY_LEFT == 0 || KEY_RIGHT == 0 || KEY_NEXT == 0 || KEY_LAST == 0))
{
SomeKeyPress_Flag = 1;
}
if(SomeKeyPress_Flag > 0)
{
KeyPressTime ++;
//计时5ms消抖
if(SomeKeyPress_Flag == 1 && KeyPressTime >= debouncing)
{
SomeKeyPress_Flag = 2;
if(KEY_UP == 0)
KeyPressNum = 1;//检测按键按下
else if(KEY_DOWN == 0)
KeyPressNum = 2;
else if(KEY_LEFT == 0)
KeyPressNum = 3;
else if(KEY_RIGHT == 0)
KeyPressNum = 4;
else if(KEY_CENTER == 0)
KeyPressNum = 5;
else if(KEY_NEXT == 0)
KeyPressNum = 6;
else if(KEY_LAST == 0)
KeyPressNum = 7;
}
//按一下就松开的状态
if((KEY_UP == 1 && KEY_DOWN == 1 && KEY_LEFT == 1 && KEY_RIGHT == 1 && KEY_NEXT == 1 && \
KEY_LAST == 1 && KEY_CENTER == 1) && KeyPressTime < AlwaysPressTime && SomeKeyPress_Flag == 2)
{
SomeKeyPress_Flag = 0;//按键松开了
if(KeyPressNum == 1)
KeyUp = onepress;
else if(KeyPressNum == 2)
KeyDown = onepress;
else if(KeyPressNum == 3)
KeyLeft = onepress;
else if(KeyPressNum == 4)
KeyRight = onepress;
else if(KeyPressNum == 5)
KeyCenter = onepress;
else if(KeyPressNum == 6)
KeyNext = onepress;
else if(KeyPressNum == 7)
KeyLast = onepress;
KeyPressTime = 0;
SomeKeyPress_Flag = 0;
// BeepTick(1,50);
}
//长按不松开的状态
if(KeyPressTime >= AlwaysPressTime && SomeKeyPress_Flag == 2)
{
if(KeyPressNum == 1) KeyUp = holdpress;
else if(KeyPressNum == 2) KeyDown = holdpress;
else if(KeyPressNum == 3) KeyLeft = holdpress;
else if(KeyPressNum == 4) KeyRight = holdpress;
else if(KeyPressNum == 5) KeyCenter = holdpress;
else if(KeyPressNum == 6) KeyNext = holdpress;
else if(KeyPressNum == 7) KeyLast = holdpress;
// BeepTick(2,50);
if(KEY_UP == 1 && KEY_DOWN == 1 && KEY_LEFT == 1 && KEY_RIGHT == 1 && \
KEY_CENTER == 1 && KEY_NEXT == 1 && KEY_LAST == 1)
{
SomeKeyPress_Flag = 0;
KeyPressTime = 0;
KeyUp = nopress;
KeyDown = nopress;
KeyLeft = nopress;
KeyRight = nopress;
KeyNext = nopress;
KeyLast = nopress;
KeyCenter = nopress;
}
}
}
}
/**
*@Name :KeyParams_Init
*@Description :KeyParams_Init
*@Param :None
*@Return :None
*@Sample :KeyParams_Init();
**/
void KeyParams_Init(void)
{
KeyUp = nopress;
KeyDown = nopress;
KeyCenter = nopress;
KeyLeft = nopress;
KeyRight = nopress;
KeyNext = nopress;
KeyLast = nopress;
}
电机
宏定义
//========================MOTOR=================================//
#define Motor_state 1 //1是一路IO控制方向一路PWM控制转速;0是两路PWM控制
#define PWM_FRE 17000 //电机频率
#define PWM_R0 PWMB_CH3_P76
#define PWM_R1 PWMB_CH1_P74
#define PWM_L1 PWMB_CH4_P77
#define PWM_L0 PWMB_CH2_P75
#define DIR_R P76
#define DIR_L P75
void MotorWrite(void);
电机驱动部分,pwm_l、pwm_r为左右电机驱动占空比;
因常见电机驱动有两种,一种是一路IO控制方向一路PWM控制转速;另一种是两路PWM控制。故将其整合,根据实际的硬件,更改参数和引脚即可
//===========================================MOTOR==========================================//
//statement
short pwm_l = 0;// 0 ~ 1000
short pwm_r = 0;
void MotorWrite(void)
{
//限幅
pwm_l = limit(pwm_l,1000);
pwm_r = limit(pwm_r,1000);
#if !Motor_state
if(pwm_l >= 0)
{
pwm_duty( PWM_L0,0);
pwm_duty( PWM_L1, pwm_l);
}
else
{
pwm_duty( PWM_L0, -pwm_l);
pwm_duty( PWM_L1, 0);
}
if(pwm_r >= 0)
{
pwm_duty( PWM_R0,0);
pwm_duty( PWM_R1, pwm_r);
}
else
{
pwm_duty( PWM_R0, -pwm_r);
pwm_duty( PWM_R1, 0);
}
}
#else
if(pwm_l >= 0)
{
DIR_L = 1;
pwm_duty( PWM_L1, pwm_l);
}
else
{
DIR_L = 0;
pwm_duty( PWM_L1, -pwm_l);
}
if(pwm_r >= 0)
{
DIR_R = 0;
pwm_duty( PWM_R1, pwm_r);
}
else
{
DIR_R = 1;
pwm_duty( PWM_R1, -pwm_r);
}
#endif
}
舵机
//========================SERVO=================================//
#define PWM_SERVO PWMA_CH3P_P24
#define SERVO_MID 6650
#define SERVO_ADDMAX 780
#define SERVO_FRE 50 //舵机频率Hz
//===========================================SERVO==========================================//
short ServoOut = 0;
short ServoAdd = 0;
void ServoWrite(void)
{
ServoAdd=limit(ServoAdd,SERVO_ADDMAX);
ServoOut = (int32)(SERVO_MID + ServoAdd);
pwm_duty(PWM_SERVO,ServoOut);
}
舵机中值(SERVO_MID )就是在该占空比下,轻轻推车,车将沿着直线行驶。
获取非常简单。可以定义一个变量,int SERVO_MID_tset=650;
在主循环里放pwm_duty(PWM_SERVO,SERVO_MID_tset);
通过不断改变SERVO_MID_tset的值,最终获取到满意的SERVO_MID。
改变SERVO_MID_tset的值的方式有很多种,最简单的就是在代码上修改,然后烧录。不断尝试。
最有效的是写一个按键菜单的人机交互,实时修改SERVO_MID_tset。
编码器
//========================ENCODER=================================//
#define ENCODER_L_TIM CTIM0_P34
#define ENCODER_L_DIR P63
#define ENCODER_R_TIM CTIM1_P35
#define ENCODER_R_DIR P73
#define RealSpeedK 8.696//0.2142 // 100/115(1024)470(4096)
//statement
typedef struct
{
float SpeedNow;
float SpeedLast;
float Acc;
float Distance;
float Distance_Min;
}WheelTypedef;
extern WheelTypedef Wheel, Wheel_L, Wheel_R;
extern int32 Encoder_L_Cnt;
extern int32 Encoder_R_Cnt;
extern int32 EncoderAll_Cnt;
void EncoderRead(void);
uint8 EncoderShow(void);
//======================================ENCODER======================================//
WheelTypedef Wheel, Wheel_L, Wheel_R;
int32 Encoder_L_Cnt = 0;
int32 Encoder_R_Cnt = 0;
int32 EncoderAll_Cnt = 0;
/**
*@Name :EncoderRead
*@Description :EncoderRead//速度单位为cm/s,距离单位为m
*@Param :None
*@Return :None
*@Sample :EncoderRead();
**/
void EncoderRead(void)
{
//获取并清空计数值
Encoder_L_Cnt = ctimer_count_read(ENCODER_L_TIM);
Encoder_R_Cnt = ctimer_count_read(ENCODER_R_TIM);
EncoderAll_Cnt += (Encoder_L_Cnt + Encoder_R_Cnt)/2;
ctimer_count_clean(ENCODER_L_TIM);
ctimer_count_clean(ENCODER_R_TIM);
if(ENCODER_L_DIR==1)Encoder_L_Cnt = - Encoder_L_Cnt;
if(ENCODER_R_DIR==0)Encoder_R_Cnt = - Encoder_R_Cnt;
//计算速度
Wheel_L.SpeedNow = (float)(Encoder_L_Cnt * RealSpeedK);
Wheel_R.SpeedNow = (float)(Encoder_R_Cnt * RealSpeedK);
//计算加速度
Wheel_L.Acc = Wheel_L.SpeedNow - Wheel_L.SpeedLast;
Wheel_R.Acc = Wheel_R.SpeedNow - Wheel_R.SpeedLast;
//速度滤波
if (myabs(Wheel_L.Acc) > 80.0f)
Wheel_L.SpeedNow = Wheel_L.SpeedNow * 0.5 + Wheel_L.SpeedLast * 0.5;
else
Wheel_L.SpeedNow = Wheel_L.SpeedNow * 0.9 + Wheel_L.SpeedLast * 0.1;
if (myabs(Wheel_R.Acc) > 80.0f)
Wheel_R.SpeedNow = Wheel_R.SpeedNow * 0.5 + Wheel_R.SpeedLast * 0.5;
else
Wheel_R.SpeedNow = Wheel_R.SpeedNow * 0.9 + Wheel_R.SpeedLast * 0.1;
//速度限幅
// Wheel_L.SpeedNow = limit(Wheel_L.SpeedNow, 500);
// Wheel_R.SpeedNow = limit(Wheel_R.SpeedNow, 500);
//速度更新
Wheel_L.SpeedLast = Wheel_L.SpeedNow;
Wheel_R.SpeedLast = Wheel_R.SpeedNow;
//距离计算
Wheel.Distance += (float)(((Wheel_L.SpeedNow + Wheel_R.SpeedNow)/2.0f)/100000.0f);
//总体速度计算
Wheel.SpeedNow = (Wheel_L.SpeedNow + Wheel_R.SpeedNow)/2.0f;
//总体速度更新
Wheel.SpeedLast = Wheel.SpeedNow;
}
ADC
//========================ADC=================================//
#define ADC1 ADC_P05
#define ADC2 ADC_P06
#define ADC3 ADC_P15
#define ADC4 ADC_P14
#define ADC5 ADC_P10
#define ADC6 ADC_P03
#define ADC7 ADC_P04
#define ADC8 ADC_P01
#define ADC9 ADC_P11
#define ADC10 ADC_P13
初始化
运行首先要初始化
写一个总的初始化函数,将所有用到的初始化包含在里面
void Decices_Init(void)
{
//adc
adc_init(ADC1,ADC_SYSclk_DIV_2);
adc_init(ADC2,ADC_SYSclk_DIV_2);
adc_init(ADC3,ADC_SYSclk_DIV_2);
adc_init(ADC4,ADC_SYSclk_DIV_2);
adc_init(ADC5,ADC_SYSclk_DIV_2);
adc_init(ADC6,ADC_SYSclk_DIV_2);
adc_init(ADC7,ADC_SYSclk_DIV_2);
adc_init(ADC8,ADC_SYSclk_DIV_2);
adc_init(ADC9,ADC_SYSclk_DIV_2);
adc_init(ADC10,ADC_SYSclk_DIV_2);
//servo
pwm_init(PWM_SERVO,SERVO_FRE,SERVO_MID);
ServoAdd = 0;
//motor
pwm_init(PWM_L1, PWM_FRE, 0);
pwm_init(PWM_R1, PWM_FRE, 0);
gpio_mode(P7_5, GPO_PP);
gpio_mode(P7_6, GPO_PP);
pwm_l = 0;
pwm_r = 0;
//encoder
ctimer_count_init(ENCODER_L_TIM);//使用方向编码器
ctimer_count_init(ENCODER_R_TIM);
//BEEP
gpio_mode(BEEP_PIN_ZF,GPO_PP);
BEEP_OFF;
}
系列文章目录
文章分为三个层次
速通版是希望通过简化的步骤搭建出寻迹小车,进而了解整个智能车是如何实现的,快速上手,为后续参与智能车竞赛做基础。
如果只是为了完成学校智能车初期培训,做出能简单循迹的小车,可以看这个速通版。
全程引导篇是讲了做出能够完赛的智能车的整个过程,大部分文章只是简单点拨一下,但是附上了相关的文章链接,方便根据自己的情况深入了解。
全程引导篇,能够带你比较系统地了解整个智能车的制作过程,推荐备赛初期或者有车模之后学习。
详细讲解篇是全程引导篇的补充,由于全程引导篇是引导性质,文章内容只是点拨,缺乏相应的原理或代码讲解,因此写详细讲解篇作为补充。
详细讲解篇会渗透在全程引导篇中。
全程引导篇
详细讲解篇
智能车模块详解——数据存储与读写(eeprom/flash)
(更新中)
评论(0)
您还未登录,请登录后发表或查看评论