• 智能小车之测速小车原理和开发


    目录

    1. 测速模块介绍

    2. 测试原理和单位换算

    3. 定时器和中断实现测速开发和调试代码

    4. 小车速度显示在OLED屏


    1. 测速模块介绍

    • 用途:广泛用于电机转速检测,脉冲计数,位置限位等。
    • 有遮挡,输出高电平;无遮挡,输出低电平
    • 接线 :VCC 接电源正极3.3-5V
    • GND 接电源负极 DO TTL开关信号输出
    • AO 此模块不起作用

    2. 测试原理和单位换算

    • 轮子走一圈,经过一个周长,C = 2x3.14x半径= 3.14 x 直径(6.5cm)
    • 对应的码盘也转了一圈,码盘有20个格子,每经过一个格子,会遮挡(高电平)和不遮挡(低电平), 那么一个脉冲就是走了 3.14 * 6.5 cm /20 = 1.0205CM
    • 定时器可以设计成一秒,统计脉冲数,一个脉冲就是1cm
    • 假设一秒有80脉冲,那么就是80cm/s

    3. 定时器和中断实现测速开发和调试代码

    定时器介绍:

    • C51中的定时器和计数器是同一个硬件电路支持的,通过寄存器配置不同,就可以将他当做定时器 或者计数器使用。
    • 确切的说,定时器和计数器区别是致使他们背后的计数存储器加1的信号不同。当配置为定时器使 用时,每经过1个机器周期,计数存储器的值就加1。而当配置为计数器时,每来一个负跳变信号 (信号从P3.4 或者P3.5引脚输入),就加1,以此达到计数的目的。
    • 标准C51有2个定时器/计数器:T0和T1。他们的使用方法一致。C52相比C51多了一个T2

    中断寄存器介绍:

    CPU能响应定时器0中断的条件:需要配置IE寄存器的bit1: ET0 bit7:EA

    • 1. ET0中断允许要置一 ET0 = 1
    • 2. EA总中断要置一 EA = 1

    测试数据通过串口发送到上位机

    1. //main.c
    2. #include "motor.h"
    3. #include "delay.h"
    4. #include "uart.h"
    5. #include "reg52.h"
    6. #include "time.h"
    7. #include "stdio.h"
    8. sbit speedIO = P3^2;//外部中断0
    9. unsigned int speedCnt = 0; //统计格子,脉冲次数
    10. extern unsigned int speed;//速度
    11. extern char signal; //主程序发速度数据的通知
    12. char speedMes[24]; //主程序发送速度数据的字符串缓冲区
    13. void Ex0Init()
    14. {
    15. EX0 = 1;//允许中断
    16. //EA = 1;在串口初始化函数中已经打开了总中断
    17. IT0 = 1;//外部中断的下降沿触发
    18. }
    19. void main()
    20. {
    21. Time0Init();//定时器0初始化
    22. UartInit();//串口相关初始化
    23. //外部中断初始化
    24. Ex0Init();
    25. while(1){
    26. if(signal){//定时器1s到点,把signal置一,主程序发送速度
    27. sprintf(speedMes,"speed:%d cm/s",speed);//串口数据的字符串拼装,speed是格子,每个格子1cm
    28. SendString(speedMes);//速度发出去
    29. signal = 0;//清0speed,下次由定时器1s后的中断处理中再置一
    30. }
    31. }
    32. }
    33. void speedHandler() interrupt 0 //外部中断处理函数
    34. {
    35. speedCnt++;//码盘转动了一个格子
    36. }
    37. //uart.c
    38. #include "reg52.h"
    39. #include "motor.h"
    40. #include "string.h"
    41. sbit D5 = P3^7;
    42. #define SIZE 12
    43. sfr AUXR = 0x8E;
    44. char buffer[SIZE];
    45. void UartInit(void) //9600bps@11.0592MHz
    46. {
    47. AUXR = 0x01;
    48. SCON = 0x50; //配置串口工作方式1,REN使能接收
    49. TMOD &= 0x0F;
    50. TMOD |= 0x20;//定时器1工作方式位8位自动重装
    51. TH1 = 0xFD;
    52. TL1 = 0xFD;//9600波特率的初值
    53. TR1 = 1;//启动定时器
    54. EA = 1;//开启总中断
    55. ES = 1;//开启串口中断
    56. }
    57. void SendByte(char mydata)
    58. {
    59. SBUF = mydata;
    60. while(!TI);
    61. TI = 0;
    62. }
    63. void SendString(char *str)
    64. {
    65. while(*str != '\0'){
    66. SendByte(*str);
    67. str++;
    68. }
    69. }
    70. //M1qian M2 hou M3 zuo M4 you
    71. void Uart_Handler() interrupt 4
    72. {
    73. static int i = 0;//静态变量,被初始化一次
    74. char tmp;
    75. if(RI)//中断处理函数中,对于接收中断的响应
    76. {
    77. RI = 0;//清除接收中断标志位
    78. tmp = SBUF;
    79. if(tmp == 'M'){
    80. i = 0;
    81. }
    82. buffer[i++] = tmp;
    83. //灯控指令
    84. if(buffer[0] == 'M'){
    85. switch(buffer[1]){
    86. case '1':
    87. goForward();
    88. break;
    89. case '2':
    90. goBack();
    91. break;
    92. case '3':
    93. goLeft();
    94. break;
    95. case '4':
    96. goRight();
    97. break;
    98. default:
    99. stop();
    100. break;
    101. }
    102. }
    103. if(i == 12) {
    104. memset(buffer, '\0', SIZE);
    105. i = 0;
    106. }
    107. }
    108. }
    109. //motor.c
    110. #include "reg52.h"
    111. sbit RightCon1A = P3^7;
    112. sbit RightCon1B = P3^3;
    113. sbit LeftCon1A = P3^4;
    114. sbit LeftCon1B = P3^5;
    115. void goForward()
    116. {
    117. LeftCon1A = 0;
    118. LeftCon1B = 1;
    119. RightCon1A = 0;
    120. RightCon1B = 1;
    121. }
    122. void goRight()
    123. {
    124. LeftCon1A = 0;
    125. LeftCon1B = 1;
    126. RightCon1A = 0;
    127. RightCon1B = 0;
    128. }
    129. void goLeft()
    130. {
    131. LeftCon1A = 0;
    132. LeftCon1B = 0;
    133. RightCon1A = 0;
    134. RightCon1B = 1;
    135. }
    136. void goBack()
    137. {
    138. LeftCon1A = 1;
    139. LeftCon1B = 0;
    140. RightCon1A = 1;
    141. RightCon1B = 0;
    142. }
    143. void stop()
    144. {
    145. LeftCon1A = 0;
    146. LeftCon1B = 0;
    147. RightCon1A = 0;
    148. RightCon1B = 0;
    149. }
    150. //time.c
    151. #include "motor.h"
    152. #include "reg52.h"
    153. extern unsigned int speedCnt;
    154. unsigned int speed;
    155. char signal = 0;
    156. unsigned int cnt = 0;
    157. void Time0Init()
    158. {
    159. //1. 配置定时器0工作模式位16位计时
    160. TMOD = 0x01;
    161. //2. 给初值,定一个0.5出来
    162. TL0=0x33;
    163. TH0=0xFE;
    164. //3. 开始计时
    165. TR0 = 1;
    166. TF0 = 0;
    167. //4. 打开定时器0中断
    168. ET0 = 1;
    169. //5. 打开总中断EA
    170. EA = 1;
    171. }
    172. void Time0Handler() interrupt 1
    173. {
    174. cnt++; //统计爆表的次数. cnt=1的时候,报表了1
    175. //重新给初值
    176. TL0=0x33;
    177. TH0=0xFE;
    178. if(cnt == 2000){//爆表2000次,经过了1s
    179. signal = 1;
    180. cnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s
    181. //计算小车的速度,也就是拿到speedCnt的值
    182. speed = speedCnt;
    183. speedCnt = 0;//1秒后拿到speedCnt个格子,就能算出这1s的速度,格子清零
    184. }
    185. }

    4. 小车速度显示在OLED屏

    使用oled模块,oled写命令

    写命令/数据的代码分析:

    • /* 1. start()
    • 2. 写入 b0111 1000 0x78
    • 3. ACK
    • 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
    • 5. ACK
    • 6. 写入指令/数据
    • 7. ACK
    • 8. STOP */

    最终小车代码整合:

    1. //main.c
    2. #include "reg52.h"
    3. #include "intrins.h"
    4. #include "Oled.h"
    5. void main()
    6. {
    7. //1. OLED初始化
    8. Oled_Init();
    9. Oled_Clear();
    10. Oled_Show_Str(2,2,"speed:35cm/s");
    11. while(1);
    12. }
    13. //oled.c
    14. #include "reg52.h"
    15. #include "intrins.h"
    16. #include "Oledfont.h"
    17. sbit scl = P1^2;
    18. sbit sda = P1^3;
    19. void IIC_Start()
    20. {
    21. scl = 0;
    22. sda = 1;
    23. scl = 1;
    24. _nop_();
    25. sda = 0;
    26. _nop_();
    27. }
    28. void IIC_Stop()
    29. {
    30. scl = 0;
    31. sda = 0;
    32. scl = 1;
    33. _nop_();
    34. sda = 1;
    35. _nop_();
    36. }
    37. char IIC_ACK()
    38. {
    39. char flag;
    40. sda = 1;//就在时钟脉冲9期间释放数据线
    41. _nop_();
    42. scl = 1;
    43. _nop_();
    44. flag = sda;
    45. _nop_();
    46. scl = 0;
    47. _nop_();
    48. return flag;
    49. }
    50. void IIC_Send_Byte(char dataSend)
    51. {
    52. int i;
    53. for(i = 0;i<8;i++){
    54. scl = 0;//scl拉低,让sda做好数据准备
    55. sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda
    56. _nop_();//发送数据建立时间
    57. scl = 1;//scl拉高开始发送
    58. _nop_();//数据发送时间
    59. scl = 0;//发送完毕拉低
    60. _nop_();//
    61. dataSend = dataSend << 1;
    62. }
    63. }
    64. void Oled_Write_Cmd(char dataCmd)
    65. {
    66. // 1. start()
    67. IIC_Start();
    68. //
    69. // 2. 写入从机地址 b0111 1000 0x78
    70. IIC_Send_Byte(0x78);
    71. // 3. ACK
    72. IIC_ACK();
    73. // 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
    74. IIC_Send_Byte(0x00);
    75. // 5. ACK
    76. IIC_ACK();
    77. //6. 写入指令/数据
    78. IIC_Send_Byte(dataCmd);
    79. //7. ACK
    80. IIC_ACK();
    81. //8. STOP
    82. IIC_Stop();
    83. }
    84. void Oled_Write_Data(char dataData)
    85. {
    86. // 1. start()
    87. IIC_Start();
    88. //
    89. // 2. 写入从机地址 b0111 1000 0x78
    90. IIC_Send_Byte(0x78);
    91. // 3. ACK
    92. IIC_ACK();
    93. // 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
    94. IIC_Send_Byte(0x40);
    95. // 5. ACK
    96. IIC_ACK();
    97. ///6. 写入指令/数据
    98. IIC_Send_Byte(dataData);
    99. //7. ACK
    100. IIC_ACK();
    101. //8. STOP
    102. IIC_Stop();
    103. }
    104. void Oled_Init(void){
    105. Oled_Write_Cmd(0xAE);//--display off
    106. Oled_Write_Cmd(0x00);//---set low column address
    107. Oled_Write_Cmd(0x10);//---set high column address
    108. Oled_Write_Cmd(0x40);//--set start line address
    109. Oled_Write_Cmd(0xB0);//--set page address
    110. Oled_Write_Cmd(0x81); // contract control
    111. Oled_Write_Cmd(0xFF);//--128
    112. Oled_Write_Cmd(0xA1);//set segment remap
    113. Oled_Write_Cmd(0xA6);//--normal / reverse
    114. Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
    115. Oled_Write_Cmd(0x3F);//--1/32 duty
    116. Oled_Write_Cmd(0xC8);//Com scan direction
    117. Oled_Write_Cmd(0xD3);//-set display offset
    118. Oled_Write_Cmd(0x00);//
    119. Oled_Write_Cmd(0xD5);//set osc division
    120. Oled_Write_Cmd(0x80);//
    121. Oled_Write_Cmd(0xD8);//set area color mode off
    122. Oled_Write_Cmd(0x05);//
    123. Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
    124. Oled_Write_Cmd(0xF1);//
    125. Oled_Write_Cmd(0xDA);//set com pin configuartion
    126. Oled_Write_Cmd(0x12);//
    127. Oled_Write_Cmd(0xDB);//set Vcomh
    128. Oled_Write_Cmd(0x30);//
    129. Oled_Write_Cmd(0x8D);//set charge pump enable
    130. Oled_Write_Cmd(0x14);//
    131. Oled_Write_Cmd(0xAF);//--turn on oled panel
    132. }
    133. void Oled_Clear()
    134. {
    135. unsigned char i,j; //-128 --- 127
    136. for(i=0;i<8;i++){
    137. Oled_Write_Cmd(0xB0 + i);//page0--page7
    138. //每个page从0列
    139. Oled_Write_Cmd(0x00);
    140. Oled_Write_Cmd(0x10);
    141. //0到127列,依次写入0,每写入数据,列地址自动偏移
    142. for(j = 0;j<128;j++){
    143. Oled_Write_Data(0);
    144. }
    145. }
    146. }
    147. void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
    148. unsigned int i;
    149. Oled_Write_Cmd(0xb0+(row*2-2)); //page 0
    150. Oled_Write_Cmd(0x00+(col&0x0f)); //low
    151. Oled_Write_Cmd(0x10+(col>>4)); //high
    152. for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){
    153. Oled_Write_Data(F8X16[i]); //写数据oledTable1
    154. }
    155. Oled_Write_Cmd(0xb0+(row*2-1)); //page 1
    156. Oled_Write_Cmd(0x00+(col&0x0f)); //low
    157. Oled_Write_Cmd(0x10+(col>>4)); //high
    158. for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){
    159. Oled_Write_Data(F8X16[i]); //写数据oledTable1
    160. }
    161. }
    162. /******************************************************************************/
    163. // 函数名称:Oled_Show_Char
    164. // 输入参数:oledChar
    165. // 输出参数:无
    166. // 函数功能:OLED显示单个字符
    167. /******************************************************************************/
    168. void Oled_Show_Str(char row,char col,char *str){
    169. while(*str!=0){
    170. Oled_Show_Char(row,col,*str);
    171. str++;
    172. col += 8;
    173. }
    174. }

  • 相关阅读:
    C++--哈希表--散列--冲突--哈希闭散列模拟实现
    MongoDB ObjectId() 是如何实现的 “千万级” 分布式唯一 ID?
    代码随想录算法训练营day30||332.重新安排行程 ||第51题. N皇后||37. 解数独
    18. 从零开始编写一个类nginx工具, 主动式健康检查源码实现
    路径穿越(Path Traversal)详解
    【Git】如何在Vscode中使用码云(Gitee)实现远程代码仓库与本地同步?(新手图文教程)
    注解汇总:Spring 常用的注解
    编程堆芯熔融物自然对流的
    P11机器学习--李宏毅笔记(Transformer Decoder)Testing部分
    node面试题
  • 原文地址:https://blog.csdn.net/m0_74712453/article/details/132717138