当前位置:   article > 正文

基于stm32的巡线(白线)小车(依据光敏板传感器测线)比赛,直角转弯等赛道分析与局部到整体代码实现_巡线小车比赛

巡线小车比赛

前言:我巡线小车代码是分割赛道的思想写的(从标志位上得知已经跑到那里了)

以下是材料选择与代码展示

1:光敏板(协会自己做的),原理是和红外检测差不多,是当其中一个光敏LED灯碰到白线会给GPIO的引脚输出低电平,此处低电平用0表示,高电平用1表示。相信有一定单片机知识的小伙伴会get到我的点。这里传感器可以用红外传感器,这个需要对应场地找对应效果最好的传感器,这里选择如果不好,可以在学校问一问做这方面比赛的学长,这里不做过多解释。 

2:L298N的直流电机驱动

     这里注意需要12V供电,自身在供电可以产生5V电压,用于某一类单片机或一些模块供电。左右分别有OUT1,OUT2,OUT3,OUT4,四路输出,其对应着四路输入,这里这个模块简单来说就是将单片机产生的对应引脚的PWM做放大处理输出在电机上,要不靠单片机GPIO产生的电压怎么驱动小车前进呢。这里是IN1=1,IN2=0,左轮正传,如果颠倒IN1与IN2的值,就会电机反转,如果IN1与IN2都为0或者都为1,电机会瞬间停止。IN3与IN4亦然。这里我们玩过51的固有的思维是用时钟中断来调节PWM(也就是速度),但是STM32却可以硬件产生精准的PWM,但只针对对应自己设定的引脚,这里用STM32就要换一种思维了,要知道PWM=0,其实就是给这个输入引脚置0了,如果PWM为最大值其实就是对应输入引脚置1了。要转变一下思维。

  3.小车的车身和所选用的轮子 

对于车身,由于我比较喜欢合金的觉得更结实,这个就看个人想法了。但是重要的一点是最好买双层的车身,因为可以把驱动等放在最底上一层,把STM32放在最上层,这样显得条理清晰,还可以使小车更稳定。轮子建议选择麦克纳姆轮,这样可以保证小车跑起来不会那么容易失控。这里可以去网上搜巡线小车套件。此处要注意,我是l298n一路控制一边的两个马达,还要注意马达接线,不要误以为是同向的两根线接在一起,不要忘了,轮子安装可是反方向安装的呦,这里很容易出错。

                        

4.电池

电池的话采用18650电池,最好买三节或四节11V到12V左右的电池组,这里我当时就犯了错误,我买的就是单个电池最后充电还要一节一节冲,十分麻烦,而且有很多时候不仅充电效率低而且有很多时候单个充电会充不上,所以这边建议买可充电的电池组。然后别忘了买搭配使用的充电器

 5.以下是自己的代码段

其中使用STM32的定时器3产生PWM的

PA.6和PA.7分别对应l298n的IN1和IN2

PB.0与PB.1分别对应l298n的IN3和IN4

                                                      

                                                                子函数 .h文件

  1. #ifndef __Timer3_H__
  2. #define __Timer3_H__
  3. #include <stm32f10x.h>
  4. void TIM3_PWM_Init(u16 arr,u16 psc);
  5. void Led_Init(void);
  6. void up(void);
  7. void left(void);
  8. void right(void);
  9. void surrounding(void);
  10. #endif

                                                                子函数.c文件

  1. #include "stm32f10x.h" // Device header
  2. #include "Delay.h"
  3. #define L1 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_3)
  4. #define L2 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_4)
  5. #define L3 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5)
  6. #define L4 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6)
  7. #define L5 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)
  8. #define L6 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8)
  9. #define L7 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)
  10. #define L8 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10)
  11. extern int arr1,arr2,arr3,arr4;
  12. extern int flag;
  13. extern int flag1;
  14. extern int counter;
  15. extern int flag2;
  16. extern int i;
  17. void left(void);
  18. void TIM3_PWM_Init(u16 arr,u16 psc)
  19. {
  20. GPIO_InitTypeDef GPIO_InitStructure;
  21. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  22. TIM_OCInitTypeDef TIM_OCInitStructure;
  23. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
  24. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO
  25. //设置引脚为复用输出功能,输出TIM3 CH的PWM脉冲波形 GPIOA.6 GPIOA.7 GPIOB.0 GPIOB.1
  26. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; //通道3 TIM_CH3 通道4 TIM_CH4
  27. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
  28. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  29. GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB
  30. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //通道1 TIM_CH1 通道2 TIM_CH2
  31. GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA
  32. //初始化TIM3定时器,我的4个pwm是用的定时器3的四个pwm通道
  33. TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  34. TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
  35. TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  36. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
  37. TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  38. //下边是四个通道的初始化,基本都是一样的
  39. //初始化TIM3 Channel 1 PWM模式
  40. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
  41. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  42. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
  43. TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC1
  44. TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器
  45. //初始化TIM3 Channel 2 PWM模式
  46. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
  47. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  48. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
  49. TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2
  50. TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器
  51. //初始化TIM3 Channel 3 PWM模式
  52. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
  53. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  54. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
  55. TIM_OC3Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC3
  56. TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR3上的预装载寄存器
  57. //初始化TIM3 Channel 4 PWM模式
  58. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
  59. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  60. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
  61. TIM_OC4Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC4
  62. TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR4上的预装载寄存器
  63. TIM_Cmd(TIM3, ENABLE); //使能TIM3
  64. }
  65. void Led_Init()
  66. {
  67. GPIO_InitTypeDef GPIO_InitStruct1;
  68. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
  69. GPIO_InitStruct1.GPIO_Mode= GPIO_Mode_IPD;
  70. GPIO_InitStruct1.GPIO_Pin=GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10;
  71. GPIO_InitStruct1.GPIO_Speed=GPIO_Speed_50MHz;
  72. GPIO_Init(GPIOB,&GPIO_InitStruct1);
  73. }
  74. void up()
  75. {
  76. while(1)
  77. {
  78. if(L3==1&&L4==0&&L5==0&&L6==1)//4,5中间两灯遇见白线
  79. {
  80. arr1=800;
  81. arr2=0;
  82. arr3=810;
  83. arr4=0;
  84. flag2=0;
  85. }
  86. else if((flag1==4&&L3==0&&L4==0&&L5==0&&L6==0)&&flag2==0)//遇见一条横白线,且标志位flag2为0
  87. {
  88. counter++;//经过的横白线次数累计
  89. flag2=1;//立即将标志位flag2变为1,防止下回在此进入此判断条件下由于stm32运算速度较快而造成多次counter累加
  90. if(counter==3)//经过第三次横白线
  91. {
  92. TIM_SetCompare1(TIM3,0);
  93. TIM_SetCompare2(TIM3,800);
  94. TIM_SetCompare3(TIM3,0);;
  95. TIM_SetCompare4(TIM3,800); //运用反冲速度减速1.89秒
  96. Delay(1890);
  97. TIM_SetCompare1(TIM3,0);
  98. TIM_SetCompare2(TIM3,899);
  99. TIM_SetCompare3(TIM3,899);;
  100. TIM_SetCompare4(TIM3,0); //辅助左转,此处可将左轮PWM变小,此处由于我硬件问题左轮反转有问题,所以改高PWM
  101. Delay(2000);//左转2秒
  102. flag1=5;//改变标志位的值,使主函数完成调用左转子函数操作
  103. break;
  104. }
  105. }
  106. else if(((L1==0&&L2==0&&L3==0&&L7==1)||(L1==0&&L3==0&&L7==0))&&flag==1)//遇见需要左转的情况,改为左转标志位
  107. {
  108. flag1=1;
  109. break;
  110. }
  111. else if((L8==0&&L7==0&&L2==1)&&flag1==2)//遇见需要右转的情况,改为右转标志位
  112. {
  113. flag1=3;
  114. break;
  115. }
  116. else if(L3==1&&L4==0&&L5==1&&L6==1)//4灯遇白线
  117. {
  118. arr1=770;
  119. arr2=0;
  120. arr3=800;
  121. arr4=0;
  122. flag2=0;
  123. }
  124. else if(L3==1&&L4==1&&L5==0&&L6==1)//5灯遇白线
  125. {
  126. arr1=800;
  127. arr2=0;
  128. arr3=780;
  129. arr4=0;
  130. flag2=0;
  131. }
  132. else if(L3==0&&L4==0&&L5==1&&L6==1)//3,4灯遇白线
  133. {
  134. arr1=760;
  135. arr2=0;
  136. arr3=800;
  137. arr4=0;
  138. }
  139. else if(L3==1&&L4==1&&L5==0&&L6==0)//5,6灯遇白线
  140. {
  141. arr1=800;
  142. arr2=0;
  143. arr3=770;
  144. arr4=0;
  145. flag2=0;
  146. }
  147. else if(L3==0&&L4==1&&L5==1&&L6==1)//3灯遇白线
  148. {
  149. arr1=750;
  150. arr2=0;
  151. arr3=800;
  152. arr4=0;
  153. flag2=0;
  154. }
  155. else if(L3==1&&L4==1&&L5==1&&L6==0)//6灯遇白线
  156. {
  157. arr1=800;
  158. arr2=0;
  159. arr3=760;
  160. arr4=0;
  161. }
  162. else if(L2==0&&L5==1)//2灯遇白线,加5灯为了增加判断准确性
  163. {
  164. arr1=740;
  165. arr2=0;
  166. arr3=800;
  167. arr4=0;
  168. flag2=0;
  169. }
  170. else if(L7==0&&L3==1)//7灯遇白线,加3灯为了增加判断准确性
  171. {
  172. arr1=800;
  173. arr2=0;
  174. arr3=750;
  175. arr4=0;
  176. flag2=0;
  177. }
  178. TIM_SetCompare1(TIM3,arr1);
  179. TIM_SetCompare2(TIM3,arr2);
  180. TIM_SetCompare3(TIM3,arr3);
  181. TIM_SetCompare4(TIM3,arr4); //赋pwm值的,以下同理
  182. }
  183. }
  184. void left()
  185. {
  186. TIM_SetCompare1(TIM3,0);
  187. TIM_SetCompare2(TIM3,0);
  188. TIM_SetCompare3(TIM3,0);
  189. TIM_SetCompare4(TIM3,0);
  190. Delay(1500);
  191. while(1)
  192. {
  193. TIM_SetCompare1(TIM3,0);
  194. TIM_SetCompare2(TIM3,750);
  195. TIM_SetCompare3(TIM3,490); //左转
  196. TIM_SetCompare4(TIM3,0);
  197. if(flag1==1)
  198. {
  199. if(L2==0||L3==0||(L4==0&&L5==1)||(L4==0&&L5==0)||(L4==1&&L5==0))//左转停止的判断
  200. {
  201. TIM_SetCompare1(TIM3,0);
  202. TIM_SetCompare2(TIM3,0);
  203. TIM_SetCompare3(TIM3,0);
  204. TIM_SetCompare4(TIM3,0);
  205. Delay(500);
  206. flag1=2;
  207. break;
  208. }
  209. }
  210. else if(flag1==5)//为了增加执行准确性
  211. {
  212. if(L5==0||(L5==0&&L6==1))//s弯左转准备开始标志
  213. {
  214. flag1=6;
  215. break;
  216. }
  217. }
  218. }
  219. }
  220. void right()//右转
  221. {
  222. TIM_SetCompare1(TIM3,0);
  223. TIM_SetCompare2(TIM3,0);
  224. TIM_SetCompare3(TIM3,0);
  225. TIM_SetCompare4(TIM3,0);
  226. Delay(1500);
  227. while(1)
  228. {
  229. TIM_SetCompare1(TIM3,520);
  230. TIM_SetCompare2(TIM3,0);
  231. TIM_SetCompare3(TIM3,0);
  232. TIM_SetCompare4(TIM3,500);
  233. if(flag1==3)
  234. {
  235. if(L6==0||L5==0||(L4==0&&L5==1)||L4==0)//右转结束标志
  236. {
  237. TIM_SetCompare1(TIM3,0);
  238. TIM_SetCompare2(TIM3,0);
  239. TIM_SetCompare3(TIM3,0);
  240. TIM_SetCompare4(TIM3,0);
  241. Delay(500);
  242. flag1=4;
  243. break;
  244. }
  245. }
  246. }
  247. }
  248. void surrounding()//转S弯的函数
  249. {
  250. while(1)
  251. {
  252. if(L3==1&&L4==0&&L5==0&&L6==1)
  253. {
  254. arr1=720;
  255. arr2=0;
  256. arr3=750;
  257. arr4=0;
  258. }
  259. else if(L2==0&&L3==0&&L4==0&&L5==0)//最后遇见横白线,意味着跑到终点,直行7秒左右停止
  260. {
  261. while(1)
  262. {
  263. if(i==0)
  264. {
  265. TIM_SetCompare1(TIM3,500);
  266. TIM_SetCompare2(TIM3,0);
  267. TIM_SetCompare3(TIM3,500);;
  268. TIM_SetCompare4(TIM3,0);
  269. Delay(7000);
  270. i=1;
  271. }
  272. TIM_SetCompare1(TIM3,0);
  273. TIM_SetCompare2(TIM3,0);
  274. TIM_SetCompare3(TIM3,0);;
  275. TIM_SetCompare4(TIM3,0);
  276. }
  277. }
  278. else if(L4==0)
  279. {
  280. arr1=510;
  281. arr2=0;
  282. arr3=780;
  283. arr4=0;
  284. }
  285. else if(L5==0)
  286. {
  287. arr1=899;
  288. arr2=0;
  289. arr3=0;
  290. arr4=450;
  291. }
  292. else if(L3==0)
  293. {
  294. arr1=440;
  295. arr2=0;
  296. arr3=780;
  297. arr4=0;
  298. }
  299. else if(L6==0)
  300. {
  301. arr1=800;
  302. arr2=0;
  303. arr3=0;
  304. arr4=430;
  305. }
  306. else if(L2==0)
  307. {
  308. arr1=430;
  309. arr2=0;
  310. arr3=780;
  311. arr4=0;
  312. }
  313. else if(L7==0)
  314. {
  315. arr1=840;
  316. arr2=0;
  317. arr3=380;
  318. arr4=0;
  319. }
  320. else if(L1==0)
  321. {
  322. arr1=420;
  323. arr2=0;
  324. arr3=780;
  325. arr4=0;
  326. }
  327. else if(L8==0)
  328. {
  329. arr1=840;
  330. arr2=0;
  331. arr3=380;
  332. arr4=0;
  333. }
  334. TIM_SetCompare1(TIM3,arr1);
  335. TIM_SetCompare2(TIM3,arr2);
  336. TIM_SetCompare3(TIM3,arr3);
  337. TIM_SetCompare4(TIM3,arr4);
  338. }
  339. }

                                                                     主函数

  1. #include "stm32f10x.h" // Device header
  2. #include "Timer3.h"
  3. #include "Delay.h"
  4. #define L1 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_3)
  5. #define L2 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_4)
  6. #define L3 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5)
  7. #define L4 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6)
  8. #define L5 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)
  9. #define L6 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8)
  10. #define L7 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)
  11. #define L8 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10)
  12. int arr1,arr2,arr3,arr4;//对左右两轮输出占空比,我是左两轮共接一路输出
  13. int counter;//记横白线数量
  14. int flag;
  15. int flag1;//记转弯等的标志位
  16. int flag2;//十字路口标志位,将要过S弯之前的横白线与直线当作十字路口
  17. int i;//最后停止时用的变量
  18. int main()
  19. {
  20. TIM3_PWM_Init(899,0);
  21. Led_Init();
  22. if(L1==0&&L2==0&&L3==0&&L4==0&&L5==0&&L6==0&&L7==0&&L8==0)//开始小车在起跑线准备开跑
  23. {
  24. TIM_SetCompare1(TIM3,899);
  25. TIM_SetCompare2(TIM3,0);
  26. TIM_SetCompare3(TIM3,899);
  27. TIM_SetCompare4(TIM3,0);
  28. Delay(2500);
  29. flag=1;
  30. }
  31. while(1)
  32. {
  33. if(flag==1)//开始直行遇左转停止
  34. {
  35. up();
  36. flag=0;//标志位为0,下次不进此if判断
  37. }
  38. else if(flag1==1)//遇见第一个需要左转的标志位
  39. {
  40. TIM_SetCompare1(TIM3,0);
  41. TIM_SetCompare2(TIM3,0);
  42. TIM_SetCompare3(TIM3,0);
  43. TIM_SetCompare4(TIM3,0);//由于速度过快此处相当于减速
  44. Delay(600);
  45. left();//调用左转子函数
  46. }
  47. else if(flag1==2)//遇见直走标志位
  48. {
  49. up();//调用直走子函数
  50. }
  51. else if(flag1==3)//遇见右转标志位
  52. {
  53. TIM_SetCompare1(TIM3,0);
  54. TIM_SetCompare2(TIM3,0);
  55. TIM_SetCompare3(TIM3,0);
  56. TIM_SetCompare4(TIM3,0);//此处各轮停止转动相当于减速
  57. Delay(800);
  58. right();//调用右转子函数
  59. }
  60. else if(flag1==4)//遇到第二个直行标志位
  61. {
  62. up();//调用直行子函数
  63. }
  64. else if(flag1==5)//遇到预备过S弯的标志位
  65. {
  66. left();//调用特定左转子函数
  67. }
  68. else if(flag1==6)//遇到开始过S弯的标志位
  69. {
  70. surrounding(); //调用过S弯的子函数
  71. }
  72. }
  73. }

                                                             延迟函数.h文件 

  1. #ifndef __DELAY_H__
  2. #define __DELAY_H__
  3. void Delay(unsigned int xms);
  4. #endif

                                                              延迟函数.c文件

  1. void Delay(unsigned int xms)
  2. {
  3. unsigned char i, j;
  4. while(xms--)
  5. {
  6. i = 2;
  7. j = 239;
  8. do
  9. {
  10. while (--j);
  11. } while (--i);
  12. }
  13. }

如果代码有问题欢迎各位大佬批评指正,后续也会分享一些其他学习的东西,如果大家觉得对你有帮助欢迎关注我呦~
 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/619181
推荐阅读
相关标签
  

闽ICP备14008679号