一款機械臂程序分享
- /***************************************************************
- @筆者:tacbo
- 功能列表:
- 1、單個舵機控制(支持PWM舵機和總線舵機)
- 2、多個舵機控制(支持PWM舵機和總線舵機)
- 3、手柄控制舵機
- 4、串口控制舵機
- 5、OLED顯示舵機的執行情況
- 6、USB一鍵下載
- 7、可支持總線MP3 總線WIFI等設備
- 8、上位機圖形化編程
- 9、控制6自由度機械臂
- ***************************************************************/
- #include "tb_rcc.h" //配置時鐘文件
- #include "tb_gpio.h" //配置IO口文件
- #include "tb_global.h" //存放全局變量
- #include "tb_delay.h" //存放延時函數
- #include "tb_type.h" //存放類型定義
- #include "tb_usart.h" //存放串口功能文件
- #include "tb_timer.h" //存放定時器功能文件
-
- #include "ADC.h" //存放ADC的
- #include "PS2_SONY.h" //存放索尼手柄
- #include "w25q64.h" //存儲芯片的操作
- #include "oled_i2c.h" //OLED文件
- #include <stdio.h> //標準庫文件
- #include <string.h> //標準庫文件
- #include <math.h> //標準庫文件
- #define VERSION 20170919 //版本定義
- #define CYCLE 1000 //PWM模塊周期
- #define PS2_LED_RED 0x73 //PS2手柄紅燈模式
- #define PS2_LED_GRN 0x41 //PS2手柄綠燈模式
- #define PSX_BUTTON_NUM 16 //手柄按鍵數目
- #define PS2_MAX_LEN 64 //手柄命令最大字節數
- #define FLAG_VERIFY 0x25 //校驗標志
- #define ACTION_SIZE 0x80 //一個動作的存儲大小
- #define W25Q64_INFO_ADDR_SAVE_STR (((8<<10)-2)<<10)//(8*1024-1)*1024 //eeprom_info結構體存儲的位置
- void system_init(void); //系統初始化
- void beep_led_dis_init(void); //開機提示
- void handle_nled(void); //LED工作指示燈提示
- void soft_reset(void); //軟件復位
- void car_pwm_set(int car_left, int car_right); //電機控制函數
- void handle_ps2(void); //手柄數據解析
- void handle_button(void); //手柄按鍵解析
- void parse_psx_buf(unsigned char *buf, unsigned char mode); //手柄按鍵解析子函數
- void handle_car(void); //搖桿數據解析控制車
- void handle_uart(void); //串口解析
- void parse_cmd(u8 *cmd); //命令解析
- void action_save(u8 *str); //動作保存函數
- int get_action_index(u8 *str); //獲取動作組序號
- void print_group(int start, int end); //打印動作組
- void int_exchange(int *int1, int *int2); //兩個int互換
- void do_group_once(int group_num); //執行動作組1次
- void handle_action(void); //處理動作組執行
- u8 check_dj_state(void); //獲取舵機的狀態
- void do_action(u8 *uart_receive_buf); //執行動作
- void replace_char(u8*str, u8 ch1, u8 ch2); //字符串字母替換
- void rewrite_eeprom(void); //寫入eeprom_info結構體
- void handle_adc(void); //處理ADC數據
- void handle_oled(void); //處理OLED數據
- u8 psx_buf[9]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; //存儲手柄的數據
- const char *pre_cmd_set_red[PSX_BUTTON_NUM] = { //紅燈模式下按鍵的配置
- "<PS2_RED01:#005P0600T2000!^$DST:5!>", //L2
- "<PS2_RED02:#005P2400T2000!^$DST:5!>", //R2
- "<PS2_RED03:#004P0600T2000!^$DST:4!>", //L1
- "<PS2_RED04:#004P2400T2000!^$DST:4!>", //R1
- "<PS2_RED05:#002P2400T2000!^$DST:2!>", //RU
- "<PS2_RED06:#003P0600T2000!^$DST:3!>", //RR
- "<PS2_RED07:#002P0600T2000!^$DST:2!>", //RD
- "<PS2_RED08:#003P2400T2000!^$DST:3!>", //RL
- "<PS2_RED09:$DJ_RECORD_DO:1!>", //SE 執行1次
- "<PS2_RED10:$DJ_RECORD_CLEAR!>", //AL 清除
- "<PS2_RED11:$DJ_RECORD!>", //AR 學習
- "<PS2_RED12:$DJR!>", //ST
- "<PS2_RED13:#001P0600T2000!^$DST:1!>", //LU
- "<PS2_RED14:#000P0600T2000!^$DST:0!>", //LR
- "<PS2_RED15:#001P2400T2000!^$DST:1!>", //LD
- "<PS2_RED16:#000P2400T2000!^$DST:0!>", //LL
- };
- const char *pre_cmd_set_grn[PSX_BUTTON_NUM] = { //綠燈模式下按鍵的配置
- "<PS2_GRN01:$!>", //L2
- "<PS2_GRN02:$!>", //R2
- "<PS2_GRN03:$!>", //L1
- "<PS2_GRN04:$!>", //R1
- "<PS2_GRN05:$!>", //RU
- "<PS2_GRN06:$!>", //RR
- "<PS2_GRN07:$!>", //RD
- "<PS2_GRN08:$!>", //RL
- "<PS2_GRN09:$!>", //SE
- "<PS2_GRN10:$!>", //AL-NO
- "<PS2_GRN11:$!>", //AR-NO
- "<PS2_GRN12:$!>", //ST
- "<PS2_GRN13:$!>", //LU
- "<PS2_GRN14:$!>", //LR
- "<PS2_GRN15:$!>", //LD
- "<PS2_GRN16:$!>", //LL
- };
- /*"D:\DreamSpark\OLED\MP3_UI.bmp",0 圖片取模數據
- unsigned char MY_PIC[] = {
- 0x00,0x03,0x05,0x09,0x11,0xFF,0x11,0x89,0x05,0xC3,0x00,0xE0,0x00,0xF0,0x00,0xF8,
- *************************
- 0x80,0xFF,0x80,0xEE,0xEE,0xEE,0xF5,0xFB,0xFF,0x9C,0xBE,0xB6,0xB6,0x88,0xFF,0x00,
- };
- */
- int i; //常用的一個臨時變量
- u8 car_dw = 1; //搖桿檔位控制
- u8 group_do_ok = 1; //動作執行完成標志
- int do_start_index; //動作組執行 起始序號
- int do_time; //動作組執行 執行次數
- int group_num_start; //動作組執行 起始序號
- int group_num_end; //動作組執行 終止序號
- int group_num_times; //動作組執行 起始變量
- u32 dj_record_time = 1000; //學習時間默認1000
- /*-------------------------------------------------------------------------------------------------------
- * 程序從這里執行
- * 這個啟動代碼 完成時鐘配置 使用外部晶振作為STM32的運行時鐘 并倍頻到72M最快的執行速率
- -------------------------------------------------------------------------------------------------------*/
- int main(void)
- {
- tb_rcc_init(); ///時鐘初始化 已經備注
-
- tb_gpio_init(); ///IO初始化 打開時鐘
-
- tb_global_init(); //全局變量初始化
-
- nled_init(); ///工作指示燈初始化
-
- // beep_init(); ///蜂鳴器初始化
-
- dj_io_init(); ///舵機IO口初始化
-
- //w25q64 init
- W25Q_Init(); //動作組存儲芯片初始化
-
- if(W25Q_TYPE != W25Q64){ //判斷是否是W25Q64芯片
- while(1)beep_on(); //如果不是則長鳴,說明芯片有問題,無法通信
- }
- W25Q_Read((u8 *)(&eeprom_info), W25Q64_INFO_ADDR_SAVE_STR, sizeof(eeprom_info)); //讀取全局變量
- if(eeprom_info.version != VERSION) { //判斷版本是否是當前版本
- eeprom_info.version = VERSION; //復制當前版本
- eeprom_info.dj_record_num = 0; //學習動作組變量賦值0
- rewrite_eeprom(); //寫入到存儲器 ///把結構體里面的內容讀取處理 然后再把內容寫進去
- }
-
- //ADC_init(); ///ADC初始化
-
- //PSX_init(); //手柄初始化 GPIO
-
-
- TIM2_Int_Init(20000, 71); ///舵機 定時器初始化 定時器中斷輸出高電平
-
- ///小車 pwm 初始化 利用了高級定時器 PWM引腳輸出
- TIM3_Pwm_Init(1000, 239);
- TIM4_Pwm_Init(1000, 239);
- car_pwm_set(0,0); //設置小車的左右輪速度為0
-
- //串口1初始化
- tb_usart1_init(115200); //配置結構體,不包括使能串口
- uart1_open(); //打開串口中斷
-
- //串口2初始化
- tb_usart2_init(115200);
- uart2_open();
-
- //串口3初始化
- tb_usart3_init(115200);
- uart3_open();
-
- //總中斷打開
- tb_interrupt_open();
-
-
- //三個串口發送測試字符
- uart1_send_str((u8 *)"uart1 check ok!");
- uart2_send_str((u8 *)"uart2 check ok!");
- uart3_send_str((u8 *)"uart3 check ok!");
-
- //總線輸出 復位總線舵機
- zx_uart_send_str((u8 *)"#255P1500T2000!");
-
- //系統滴答時鐘初始化
- SysTick_Int_Init();
-
- //蜂鳴器LED 名叫閃爍 示意系統啟動
- beep_led_dis_init();
-
-
- //執行預存命令
- if(eeprom_info.pre_cmd[PRE_CMD_SIZE] == FLAG_VERIFY) {
- if(eeprom_info.pre_cmd[0] == '
- ) {
- memset(uart_receive_buf, 0, sizeof(uart_receive_buf));
- strcpy((char *)uart_receive_buf, (char *)eeprom_info.pre_cmd);///把儲存的數據讀取出來
- uart1_mode = 1;
- uart1_get_ok = 1;
- uart1_send_str(uart_receive_buf);
- }
- }
-
- //OLED初始化
- //OLED_Init();
-
- while(1) //parse_cmd(uart_receive_buf); //解析命令模式 串口改變變量和按鍵 公用這個函數 解析動作,解析動作只是改變舵機的變量,舵機控制在中斷函數
- {
- handle_nled(); //處理信號燈 ///利用系統內部定時器SysTick
- handle_ps2(); //處理手柄 初始化 把手柄的數據讀取出來
-
-
- handle_button(); //處理手柄按鈕 //do_action(uart_receive_buf); 獲取按鍵狀態 控制舵機
- handle_car(); //處理搖桿控制小車
-
- handle_uart(); //處理串口接收數據 //定義一個指針,可以被多個函數調用 中斷接收串口數據 do_action(uart_receive_buf);
- handle_action(); //處理動作組 main 865
-
- handle_adc(); //處理ADC
- handle_oled(); //處理OLED顯示
- }
- }
- //開機指示函數,蜂鳴器和工作指示燈鳴3聲作為開機指示
- void beep_led_dis_init(void) {
- beep_on();nled_on();tb_delay_ms(100);beep_off();nled_off();tb_delay_ms(100);
- beep_on();nled_on();tb_delay_ms(100);beep_off();nled_off();tb_delay_ms(100);
- beep_on();nled_on();tb_delay_ms(100);beep_off();nled_off();tb_delay_ms(100);
- }
- ///工作指示燈處理,間隔1000MS閃爍一次
- void handle_nled(void) {
- static u32 time_count=0;
- static u8 flag = 0;
- if(systick_ms-time_count > 1000) {
- time_count = systick_ms;
- if(flag) {
- nled_on();
- } else {
- nled_off();
- }
- flag= ~flag;
- }
- }
- //軟件復位函數,調用后單片機自動復位
- void soft_reset(void) {
- __set_FAULTMASK(1);
- NVIC_SystemReset();
- }
- //小車控制函數
- //參數 左輪速度和右輪速度 范圍 -1000 到 1000
- void car_pwm_set(int car_left, int car_right) {
-
- if(car_left >= CYCLE)car_left = CYCLE-1;
- else if(car_left <= -CYCLE)car_left = -CYCLE+1;
- else if(car_left == 0)car_left = 1;
-
- if(car_right >= CYCLE)car_right = CYCLE-1;
- else if(car_right <= -CYCLE)car_right = -CYCLE+1;
- else if(car_right == 0)car_right = 1;
-
- //car_left = car_left/car_dw;
- //car_right = car_right/car_dw;
-
- if(car_right>0) {
- TIM_SetCompare4(TIM4,1);
- TIM_SetCompare3(TIM4,car_right); ///設置CCR,初始化已經設置的CCR沒影響 占空比
- }
- else
- {
- TIM_SetCompare4(TIM4,-car_right);
- TIM_SetCompare3(TIM4,1);
-
- }
- if(car_left>0)
- {
- TIM_SetCompare4(TIM3,1);
- TIM_SetCompare3(TIM3,car_left); ///設置占空比 CCR PWM比較寄存器
- }
- else
- {
- TIM_SetCompare4(TIM3,-car_left);
- TIM_SetCompare3(TIM3,1);
-
- }
- // //總線馬達設置
- // sprintf((char *)cmd_return, "#0233P%dT0!#034P%dT0!",
- // (int)(1500+car_left), (int)(1500+car_right));
- // zx_uart_send_str(cmd_return);
-
- return;
- }
- //處理手柄
- void handle_ps2(void)
- {
- static u32 systick_ms_bak = 0;
- //每20ms處理1次
- if(systick_ms - systick_ms_bak < 20)
- {
- return;
- }
-
- systick_ms_bak = systick_ms;
-
- //讀寫手柄數據
- psx_write_read(psx_buf); //把psx buf數據處理
-
- #if 0
- //測試手柄數據,1為打開 0為關閉
- sprintf((char *)cmd_return, "0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x\r\n",
- (int)psx_buf[0], (int)psx_buf[1], (int)psx_buf[2], (int)psx_buf[3],
- (int)psx_buf[4], (int)psx_buf[5], (int)psx_buf[6], (int)psx_buf[7], (int)psx_buf[8]);
- uart1_send_str(cmd_return);
- #endif
-
- return;
- }
- //處理手柄按鍵
- void handle_button(void)
- {
- static unsigned char psx_button_bak[2] = {0};///首先對這個數組清空
- //對比兩次獲取的按鍵值是否相同 ,相同就不處理,不相同則處理
-
- if((psx_button_bak[0] == psx_buf[3]) //psx buf 是已經獲取的值
- && (psx_button_bak[1] == psx_buf[4]))
- {
-
- }
-
-
- else {
- //處理buf3和buf4兩個字節,這兩個字節存儲這手柄16個按鍵的狀態
- parse_psx_buf(psx_buf+3, psx_buf[1]); ///獲取狀態 把按鍵獲取的數值 輸入到函數處理
- psx_button_bak[0] = psx_buf[3];//把這兩個值 放入到數組
- psx_button_bak[1] = psx_buf[4];
- }
- return;
- }
- //處理手柄按鍵字符,buf為字符數組,mode是指模式 主要是紅燈和綠燈模式 獲取按鈕狀態
- void parse_psx_buf(unsigned char *buf, unsigned char mode) ///mode就是紅燈或者綠燈
- {
- u8 i, pos = 0;
- static u16 bak=0xffff, temp, temp2;
- temp = (buf[0]<<8) + buf[1];
-
- if(bak != temp)
- {
- temp2 = temp;
- temp &= bak;
- for(i=0;i<16;i++) {//16個按鍵一次輪詢
- if((1<<i) & temp)
- {
- }
-
-
- else
- {
- if((1<<i) & bak)
- { //press 表示按鍵按下了
-
- memset(uart_receive_buf, 0, sizeof(uart_receive_buf)); ///把數組清空
- if(mode == PS2_LED_RED)
- {
- memcpy((char *)uart_receive_buf, (char *)pre_cmd_set_red[i], strlen(pre_cmd_set_red[i]));
- }
- else if(mode == PS2_LED_GRN)
- {
- memcpy((char *)uart_receive_buf, (char *)pre_cmd_set_grn[i], strlen(pre_cmd_set_grn[i]));
- } //把按下的按鍵對應的數值傳遞給接收數組 用來解析
-
- else continue;
-
- pos = str_contain_str(uart_receive_buf, (u8 *)"^"); ///通過按鍵 解析模式
- if(pos)
- uart_receive_buf[pos-1] = '\0';
-
- if(str_contain_str(uart_receive_buf, (u8 *)"[ DISCUZ_CODE_0 ]quot;)) //命令模式
- {
- uart1_close();
- uart1_get_ok = 0;
- strcpy((char *)cmd_return, (char *)uart_receive_buf+11);
- strcpy((char *)uart_receive_buf, (char *)cmd_return);
- uart1_get_ok = 1;
- uart1_open();
- uart1_mode = 1;
- }
-
-
-
- else if(str_contain_str(uart_receive_buf, (u8 *)"#"))
- {
- uart1_close();
- uart1_get_ok = 0;
- strcpy((char *)cmd_return, (char *)uart_receive_buf+11);
- strcpy((char *)uart_receive_buf,(char *) cmd_return);
- uart1_get_ok = 1;
- uart1_open();
- uart1_mode = 2;
- }
-
- //uart1_send_str(uart_receive_buf);
- bak = 0xffff;
- } else {//release 表示按鍵松開了
-
- memset(uart_receive_buf, 0, sizeof(uart_receive_buf));
- if(mode == PS2_LED_RED) {
- memcpy((char *)uart_receive_buf, (char *)pre_cmd_set_red[i], strlen(pre_cmd_set_red[i]));
- } else if(mode == PS2_LED_GRN) {
- memcpy((char *)uart_receive_buf, (char *)pre_cmd_set_grn[i], strlen(pre_cmd_set_grn[i]));
- } else continue;
-
- pos = str_contain_str(uart_receive_buf, (u8 *)"^");
- if(pos) {
- if(str_contain_str(uart_receive_buf+pos, (u8 *)"[ DISCUZ_CODE_0 ]quot;)) {
- //uart1_close();
- //uart1_get_ok = 0;
- strcpy((char *)cmd_return, (char *)uart_receive_buf+pos);
- cmd_return[strlen((char *)cmd_return) - 1] = '\0';
- strcpy((char *)uart_receive_buf, (char *)cmd_return);
- parse_cmd(uart_receive_buf);
- //uart1_get_ok = 1;
- //uart1_mode = 1;
- } else if(str_contain_str(uart_receive_buf+pos, (u8 *)"#")) {
- //uart1_close();
- //uart1_get_ok = 0;
- strcpy((char *)cmd_return, (char *)uart_receive_buf+pos);
- cmd_return[strlen((char *)cmd_return) - 1] = '\0';
- strcpy((char *)uart_receive_buf, (char *)cmd_return);
- do_action(uart_receive_buf);
- //uart1_get_ok = 1;
- //uart1_mode = 2;
- }
- //uart1_send_str(uart_receive_buf);
- }
- }
- }
- }
- bak = temp2;
- beep_on();mdelay(10);beep_off();
- }
- return;
- }
- //int型 取絕對值函數
- int abs_int(int int1) {
- if(int1 > 0)return int1;
- return (-int1);
- }
- //處理小車函數 主要處理搖桿的數據 這里 8為左搖桿 6為右搖桿
- void handle_car(void)
- {
- static int car_left, car_right, car_left_bak, car_right_bak;
-
- if(psx_buf[1] != PS2_LED_RED)return;
-
- if(abs_int(127 - psx_buf[8]) < 5 )
- psx_buf[8] = 127;
-
- if(abs_int(127 - psx_buf[6]) < 5 )
- psx_buf[6] = 127;
-
- car_left = (127 - psx_buf[8]) * 8;
- car_right = (127 - psx_buf[6]) * 8;
-
- // if(abs_int(car_left_bak-car_left) < 20 && abs_int(car_right_bak-car_right) < 20)return;
- // if(abs_int(car_left_bak-car_left) > 40)car_left_bak = car_left;
- // if(abs_int(car_right_bak-car_right) > 40)car_right_bak = car_right;
-
- if(car_left != car_left_bak || car_right != car_right_bak)
- {
- car_pwm_set(car_left, car_right); ///把速度傳送到定時器PWM寄存器
- car_left_bak = car_left;
- car_right_bak = car_right;
- }
- }
- //處理串口接收到的數據 ///首先通過中斷獲取串口的數據
- void handle_uart(void)
-
- ///串口接收中斷,判斷中斷標志位,進入中斷函數,把數據保存在數組,判斷數組內容,返回uart1_get_ok
- ///當接收到數據之后,馬上執行串口解析動作,判斷數據,執行動作
- {
- if(uart1_get_ok)
-
- {
- if(uart1_mode == 1) //命令模式
- {
- //uart1_send_str("cmd:");
- //uart1_send_str(uart_receive_buf);
- parse_cmd(uart_receive_buf); //解析命令模式
- }
-
-
- else if(uart1_mode == 2) ///單個命令 只有一種模式
- { //單個舵機調試
- //uart1_send_str("sig:");
- //uart1_send_str(uart_receive_buf);
- do_action(uart_receive_buf); ///處理接收數據內容
- }
-
- else if(uart1_mode == 3)
- { //多路舵機調試
- //uart1_send_str("group:");
- //uart1_send_str(uart_receive_buf);
- do_action(uart_receive_buf);
- }
-
- else if(uart1_mode == 4)
- { //存儲模式
- //uart1_send_str("save:");
- //uart1_send_str(uart_receive_buf);
- action_save(uart_receive_buf);
- }
-
- uart1_mode = 0;
- uart1_get_ok = 0;
- }
- return;
- }
- /*
- 所有舵機停止命令:$DST!
- 第x個舵機停止命令:$DST:x!
- 單片機重啟命令:$RST!
- 檢查動作組x到y組命令:$CGP:x-y!
- 執行第x個動作:$DGS:x!
- 執行第x到y組動作z次:$DGT:x-y,z!
- 小車左輪x速度,右輪y速度:$DCR:x,y!
- 所有舵機復位命令:$DJR!
- 獲取應答信號:$GETA!
- */
- //命令解析函數
- void parse_cmd(u8 *cmd)
- {
- int pos, i, index, int1, int2;
- u8 temp_buf[160];
- u32 long1;
-
- //uart1_send_str(cmd);
-
- if(pos = str_contain_str(uart_receive_buf, (u8 *)"$DST!"), pos) ///接收信息比較 所有舵機停止命令:$DST!
- {
- group_do_ok = 1; //動作執行完成標志
-
- for(i=0;i<DJ_NUM;i++) ///停止舵機
- ……………………
- …………限于本文篇幅 余下代碼請從51黑下載附件…………
復制代碼
代碼下載:
注釋stm32單片機之串口1解析控制舵機.7z
(1.18 MB, 下載次數: 10)
2021-9-26 23:03 上傳
點擊文件名下載附件
閱讀權限: 255 下載積分: 黑幣 -5
|