1, Project introduction.
I had the idea of making a smart car for a long time, but I haven't taken any action due to limited time and lack of money. Thanks to the trust of Tencent TencentOS Tiny team, I have given such an opportunity. Although it is far from the function of my own imagination, it has at least taken an important step, More importantly, I would like to thank you for learning TencentOS Tiny real-time operating system and i.MX RT series application processors.
The initial idea is to build a patrol car that can transmit video in real time. However, due to the project time and current hardware constraints, it can only be said to build a remote control car based on TencentOS Tiny. It mainly realizes the following functions:
- Based on Tencent cloud platform, Tencent connected H5 panel is used to control the car action.
- The traveling of the trolley includes forward, backward, left turn and right turn.
- The trolley avoids obstacles and uses the ultrasonic module to measure the distance. In the process of moving forward, when the distance is less than 30 cm, the trolley brakes automatically and stops moving forward.
- Report the distance of obstacles ahead in real time and display it in H5 panel.
2, Overall project architecture
3, Hardware composition of trolley.
1. Trolley chassis. Four DC motor drives.
2. TencentOS Tiny AIoT development kit
Development board features:
- Built in TencentOS Tiny open source Internet of things operating system.
- The RT1062 processor used in the core board belongs to i.MX RT series MCU, which is a cross-border processor launched by NXP. Cross-border means that the positioning of this series of MCU is neither a traditional microcontroller nor a traditional microprocessor. I.MX RT series MCU combines the advantages of the two, which not only has high frequency (maximum dominant frequency 600M), high processing performance, but also has rapid interrupt response High real-time characteristics.
- 1M RAM 16M SDRAM 64MB qspi flash 128MB spi flash.
- Onboard Type-C interface CMSIS DAP simulator.
- On board PCIE interface, which can expand 4G Internet of things modules.
- The onboard internet of things Club WAN Interface interface can support NB IOT, WiFi, 4G cat1, LoRa and other modules.
- The onboard internet of things club E53 Interface interface can expand the whole series of E53 sensors.
- Onboard standard 24P DVP camera interface, which can support up to 5 megapixel camera.
- Onboard RGB display interface, which can convert HDMI output.
- On board high-performance audio decoding chip can be used for speech recognition test.
- SD card, user key and SPI Flash are reserved.
3. HG7881CP four way DC motor drive module.
4. HC-SR04 ultrasonic ranging module.
Working principle of HC-SR04 module:
(1) IO trigger ranging is adopted to provide high-level signal of at least 10us;
(2) The module automatically sends 8 40khz square waves and automatically detects whether there is signal return;
(3) When a signal returns, a high level is output through IO. The duration of high level is the time from transmission to return
Test distance = (high level time * sound speed (340M/S))/2;
5. ESP8266 module
4, Knowledge points involved in the project.
1. Utilization of Tencent cloud platform, H5 panel configuration and debugging.
2. Use of TencentOS Tiny.
3. Understand MQTT protocol and cJSON data format.
4. Use the ESP8266 module to send and receive data to Tencent cloud platform.
5. Use of the official development tool MCUXpresso IDE.
6. GPIO configuration of RT1062.
7. RT1062 uses input capture to measure pulse width.
5, Software introduction.
1. TencentOS tiny Internet of things operating system
TencentOS tiny is a real-time operating system developed by Tencent for the field of Internet of things. It has been donated to the open atom open source foundation for incubation. It has the characteristics of low power consumption, low resource occupation, modularization, safety and reliability, and can effectively improve the development efficiency of Internet of things terminal products. TencentOS tiny provides a simplified RTOS kernel. The kernel components can be tailored and configured, and can be quickly transplanted to a variety of mainstream MCU (such as NXP Arm Cortex-M full series) and module chips. Moreover, rich IOT components are provided based on RTOS kernel, and mainstream IOT protocol stacks (such as COAP / mqtt / TLS / dtls / lorawan / Nb IOT) are integrated internally, which can help IOT terminal devices and services quickly access Tencent cloud IOT platform.
·Little resource occupation
Tencentos tiny kernel has the characteristics of ultra-low resource occupation, with ram of 0.8kb and ROM of 1.8kb; In actual scenarios such as smoke and infrared, the resource occupation of tencentos tiny is only 2.69kb ram and 12.38kb ROM.
·Efficient power management framework
It completely includes the power consumption management of MCU and peripheral equipment. Users can choose a reference low-power scheme according to the business scenario, which can effectively reduce the power consumption of the equipment and prolong the service life of the equipment.
·Automatic migration tool
TencentOS tiny provides a variety of compiler rapid migration guides and migration tools, which can realize one click migration to new hardware development board, save time and effort, and effectively improve development efficiency.
·Last screen debugging tool
TencentOS tiny can automatically obtain the fault site information and keep it in the end side storage device. After triggering the restart, it will automatically upload the fault information, which can effectively solve the problem of obtaining fault information of remote Internet of things devices and improve the efficiency of fault analysis and resolution.
·Safety classification scheme
TencentOS tiny provides multiple levels of IoT security solutions. You can choose appropriate security solutions according to business scenarios and cost requirements to facilitate customers to effectively balance security needs and cost control.
2. Data analysis issued by cloud platform.
static void tos_topic_handler(void* client, message_data_t* msg) { (void) client; cJSON* cjson_root = NULL; cJSON* cjson_status = NULL; cJSON* cjson_params = NULL;//add zyd char* status = NULL; // char* cmd_value = NULL; //add zyd // char cmd_value = 0; //add zyd k_event_flag_t event_flag = report_fail; /* ��ӡ��־ */ MQTT_LOG_I("-----------------------------------------------------------------------------------"); MQTT_LOG_I("%s:%d %s()...\ntopic: %s, qos: %d. \nmessage:\n\t%s\n", __FILE__, __LINE__, __FUNCTION__, msg->topic_name, msg->message->qos, (char*)msg->message->payload); MQTT_LOG_I("-----------------------------------------------------------------------------------\n"); /* ʹ��cjson�����ϱ���Ӧ���� */ cjson_root = cJSON_Parse((char*)msg->message->payload); if (cjson_root == NULL) { printf("report reply message parser fail\r\n"); event_flag = report_fail; goto exit; } /*Analyze and issue instructions */ cjson_params = cJSON_GetObjectItem(cjson_root, "params"); if (cjson_params != NULL) { cJSON* cjson_onoff = cJSON_GetObjectItem(cjson_params,"onoff"); if(cjson_onoff != NULL) car_cmd.onoff = cjson_onoff->valueint; cJSON* cjson_drive = cJSON_GetObjectItem(cjson_params,"drive"); if(cjson_drive != NULL) car_cmd.drive = cjson_drive->valueint; cJSON* cjson_reverse = cJSON_GetObjectItem(cjson_params,"reverse"); if(cjson_reverse != NULL) car_cmd.reverse = cjson_reverse->valueint; cJSON* cjson_right = cJSON_GetObjectItem(cjson_params,"right"); if(cjson_right != NULL) car_cmd.right = cjson_right->valueint; cJSON* cjson_left = cJSON_GetObjectItem(cjson_params,"left"); if(cjson_left != NULL) car_cmd.left = cjson_left->valueint; } /* ��ȡstatus״̬ */ cjson_status = cJSON_GetObjectItem(cjson_root, "status"); status = cJSON_GetStringValue(cjson_status); if (cjson_status == NULL || status == NULL) { printf("report reply status parser fail\r\n"); event_flag = report_fail; goto exit; } /* �ж�status״̬ */ if (strstr(status,"success")) { event_flag = report_success; }else { event_flag = report_fail; } exit: cJSON_Delete(cjson_root); cjson_root = NULL; status = NULL; tos_event_post(&report_result_event, event_flag); return; }
3. Upload data on cloud platform.
void mqttclient_task(void) { int error; int lightness = 0; mqtt_client_t *client = NULL; mqtt_message_t msg; k_event_flag_t match_flag; char host_ip[20]; memset(&msg, 0, sizeof(msg)); #ifdef USE_ESP8266 esp8266_sal_init(esp8266_port); // esp8266_join_ap("TencentOS", "tencentostiny"); esp8266_join_ap("TP-LINK_AF26", "xn20190213"); // esp8266_join_ap("TP-LINK_EA9E", "20160130"); #endif #ifdef USE_EC600S ec600s_sal_init(HAL_UART_PORT_0); #endif mqtt_log_init(); client = mqtt_lease(); tos_event_create(&report_result_event, (k_event_flag_t)0u); /* Domain Format: <your product ID>.iotcloud.tencentdevices.com */ tos_sal_module_parse_domain("O0CBVXMB0X.iotcloud.tencentdevices.com",host_ip,sizeof(host_ip)); /* These infomation is generated by mqtt_config_gen.py tool in "TencentOS-tiny\tools" directory. */ mqtt_set_port(client, "1883"); mqtt_set_host(client, host_ip); mqtt_set_client_id(client, "O0CBVXMB0XCar"); mqtt_set_user_name(client, "O0CBVXMB0XCar;21010406;12365;4294967295"); mqtt_set_password(client, "63d7adbf99fe8a2c93876da22dd150bdc6f78dea;hmacsha1"); mqtt_set_clean_session(client, 1); error = mqtt_connect(client); //MQTT_LOG_D("mqtt connect error is %#0x", error); error = mqtt_subscribe(client, "$thing/down/property/O0CBVXMB0X/Car", QOS0, tos_topic_handler); // MQTT_LOG_D("mqtt subscribe error is %#0x", error); while (1) { memset(&msg, 0, sizeof(msg)); // snprintf(report_buf, sizeof(report_buf), REPORT_DATA_TEMPLATE, lightness++); snprintf(report_buf, sizeof(report_buf), REPORT_DATA_TEMPLATE, distance); // if (lightness > 100) { // lightness = 0; // } msg.qos = QOS0; msg.payload = (void *) report_buf; error = mqtt_publish(client, "$thing/up/property/O0CBVXMB0X/Car", &msg); // MQTT_LOG_D("mqtt publish error is %#0x", error); tos_event_pend(&report_result_event, report_success|report_fail, &match_flag, TOS_TIME_FOREVER, TOS_OPT_EVENT_PEND_ANY | TOS_OPT_EVENT_PEND_CLR); if (match_flag == report_success) { // printf("report to Tencent IoT Explorer success\r\n"); }else if (match_flag == report_fail){ // printf("report to Tencent IoT Explorer fail\r\n"); } /////////////////////// tos_task_delay(1000); } }
4. Motor control and Ultrasonic Ranging procedures. (refer to the routine in the development board of wildfire i.MXRT1052 for the measurement procedure of pulse width)
#include "pin_mux.h" #include "board.h" #include "fsl_gpio.h" #include "fsl_common.h" #include "fsl_iomuxc.h" #include "fsl_debug_console.h" #include "clock_config.h" #include "fsl_gpt.h" #include "car_contrl.h" #include "tos_k.h" #include "pad_config.h" #include "bsp_nvic.h" //#define SR04_ECHO_GPIO GPIO1 //#define SR04_ECHO_GPIO_PIN 01U #define SR04_TRIG_GPIO GPIO1 #define SR04_TRIG_GPIO_PIN 02U /******************************************************************************* * Variables ******************************************************************************/ /* Whether the SW is turned on */ volatile bool g_InputSignal = false; /* Symbols to be used with GPIO driver */ #define MOTOR_D1_GPIO GPIO2 /*!< GPIO peripheral base pointer */ #define MOTOR_D1_GPIO_PIN 30U /*!< GPIO pin number */ /* Symbols to be used with GPIO driver */ #define MOTOR_D2_GPIO GPIO2 /*!< GPIO peripheral base pointer */ #define MOTOR_D2_GPIO_PIN 31U /*!< GPIO pin number */ /* Symbols to be used with GPIO driver */ #define MOTOR_B1_GPIO GPIO1 /*!< GPIO peripheral base pointer */ #define MOTOR_B1_GPIO_PIN 16U /*!< GPIO pin number */ /* Symbols to be used with GPIO driver */ #define MOTOR_B2_GPIO GPIO1 /*!< GPIO peripheral base pointer */ #define MOTOR_B2_GPIO_PIN 17U /*!< GPIO pin number */ #define MOTOR_REVERSE_EN() GPIO_PinWrite(MOTOR_D1_GPIO, MOTOR_D1_GPIO_PIN, 1U);\ GPIO_PinWrite(MOTOR_D2_GPIO, MOTOR_D2_GPIO_PIN, 0U);\ GPIO_PinWrite(MOTOR_B1_GPIO, MOTOR_B1_GPIO_PIN, 1U);\ GPIO_PinWrite(MOTOR_B2_GPIO, MOTOR_B2_GPIO_PIN, 0U) #define MOTOR_DRIVE_EN() GPIO_PinWrite(MOTOR_D1_GPIO, MOTOR_D1_GPIO_PIN, 0U);\ GPIO_PinWrite(MOTOR_D2_GPIO, MOTOR_D2_GPIO_PIN, 1U);\ GPIO_PinWrite(MOTOR_B1_GPIO, MOTOR_B1_GPIO_PIN, 0U);\ GPIO_PinWrite(MOTOR_B2_GPIO, MOTOR_B2_GPIO_PIN, 1U) #define MOTOR_RIGHT_EN() GPIO_PinWrite(MOTOR_B1_GPIO, MOTOR_B1_GPIO_PIN, 0U);\ GPIO_PinWrite(MOTOR_B2_GPIO, MOTOR_B2_GPIO_PIN, 1U) #define MOTOR_LEFT_EN() GPIO_PinWrite(MOTOR_D1_GPIO, MOTOR_D1_GPIO_PIN, 0U);\ GPIO_PinWrite(MOTOR_D2_GPIO, MOTOR_D2_GPIO_PIN, 1U) #define MOTOR_STOP() GPIO_PinWrite(MOTOR_D1_GPIO, MOTOR_D1_GPIO_PIN, 0U);\ GPIO_PinWrite(MOTOR_D2_GPIO, MOTOR_D2_GPIO_PIN, 0U);\ GPIO_PinWrite(MOTOR_B1_GPIO, MOTOR_B1_GPIO_PIN, 0U);\ GPIO_PinWrite(MOTOR_B2_GPIO, MOTOR_B2_GPIO_PIN, 0U); #define SR04_TRIG_ON() GPIO_PinWrite(SR04_TRIG_GPIO, SR04_TRIG_GPIO_PIN, 1U); #define SR04_TRIG_OFF() GPIO_PinWrite(SR04_TRIG_GPIO, SR04_TRIG_GPIO_PIN, 0U); //*****************// ctrl_cmd_t car_cmd; volatile uint64_t timer = 0; volatile int distance = 0; int trig_cnt = 0; // Timer input captures user-defined variable structure definition volatile GPT_ICUserValueTypeDef GPT_ICUserValueStructure = {0,0,0,0,0}; /** * @brief Configure GPT related pin functions * @param nothing * @retval nothing */ void GPT_GPIO_Config(void) { /*Define GPIO pin configuration structure*/ gpio_pin_config_t gpt_config; IOMUXC_SetPinMux(IOMUXC_GPIO_EMC_40_GPT2_CAPTURE2, 0U); IOMUXC_SetPinConfig(IOMUXC_GPIO_EMC_40_GPT2_CAPTURE2, GPT_COMPARE_PAD_CONFIG_DATA); gpt_config.direction = kGPIO_DigitalInput; //Input mode //gpt_config.outputLogic = 0; // Default high level gpt_config.interruptMode = kGPIO_NoIntmode; //Do not use interrupt /* Initialize GPT COMPARE1 GPIO */ GPIO_PinInit(GPT2_CAPTURE2_GPIO, GPT2_CAPTURE2_GPIO_PIN, &gpt_config); } /** * @brief Configure GPT working mode * @param nothing * @retval nothing */ void GPT_Config(void) { gpt_config_t gptConfig; /*Initialize GPT pin*/ GPT_GPIO_Config(); /*GPT Clock settings for*/ CLOCK_SetMux(kCLOCK_PerclkMux, EXAMPLE_GPT_CLOCK_SOURCE_SELECT); CLOCK_SetDiv(kCLOCK_PerclkDiv, EXAMPLE_GPT_CLOCK_DIVIDER_SELECT); /*Initialize GPT*/ GPT_GetDefaultConfig(&gptConfig); GPT_Init(EXAMPLE_GPT, &gptConfig); /* Set clock frequency division */ GPT_SetClockDivider(EXAMPLE_GPT, GPT_DIVIDER); /*Set bit input mode*/ GPT_SetInputOperationMode(EXAMPLE_GPT,kGPT_InputCapture_Channel2,kGPT_InputOperation_RiseEdge); /*Enable input capture interrupt*/ GPT_EnableInterrupts(EXAMPLE_GPT, kGPT_InputCapture2InterruptEnable); /*Enable overflow interrupt*/ GPT_EnableInterrupts(EXAMPLE_GPT,kGPT_RollOverFlagInterruptEnable); /*Set interrupt priority,*/ set_IRQn_Priority(GPT_IRQ_ID,Group4_PreemptPriority_6, Group4_SubPriority_0); /*Enable interrupt*/ EnableIRQ(GPT_IRQ_ID); /* Start timer */ GPT_StartTimer(EXAMPLE_GPT); } /*Define interrupt service function*/ void EXAMPLE_GPT_IRQHandler(void) { //tos_knl_irq_enter(); //PRINTF("GPIO_GetPinsInterrupt_zyd3333\r\n"); /* *When the period of the signal to be captured is greater than the longest fixed time of the timer, the timer will overflow and generate an update interrupt *At this time, we need to add the longest timing period to the time of capturing the signal */ if ( GPT_GetStatusFlags(EXAMPLE_GPT,kGPT_RollOverFlag) != false ) { if ( GPT_ICUserValueStructure.Capture_StartFlag != 0 ) { GPT_ICUserValueStructure.Capture_Period ++; } GPT_ClearStatusFlags(EXAMPLE_GPT, kGPT_RollOverFlag); } /*Capture interrupt*/ if (GPT_GetStatusFlags(EXAMPLE_GPT,kGPT_InputCapture2Flag) != false) { if(GPT_ICUserValueStructure.Capture_FinishFlag != 1) { /*First capture*/ if ( GPT_ICUserValueStructure.Capture_StartFlag == 0 ) { /*Clear overflow times*/ GPT_ICUserValueStructure.Capture_Period = 0; /*Read current count value*/ GPT_ICUserValueStructure.Capture_CcrValue_1 = GPT_GetInputCaptureValue(EXAMPLE_GPT,kGPT_InputCapture_Channel2); //PRINTF("GPIO_GetPinsInterrupt_zyd1111 RiseEdge %d \r\n",GPT_ICUserValueStructure.Capture_CcrValue_1); /*When the rising edge is captured for the first time, the captured edge is configured as the rising edge*/ GPT_SetInputOperationMode(EXAMPLE_GPT,kGPT_InputCapture_Channel2,kGPT_InputOperation_FallEdge); /*Start capture flag set to 1*/ GPT_ICUserValueStructure.Capture_StartFlag = 1; } /*Rising edge capture interrupt, second capture*/ else { /*Get the value of the capture comparison register, which is the value of the captured high-level time*/ GPT_ICUserValueStructure.Capture_CcrValue_2 = GPT_GetInputCaptureValue(EXAMPLE_GPT,kGPT_InputCapture_Channel2); //PRINTF("GPIO_GetPinsInterrupt_zyd2222 FallEdge%d \r\n",GPT_ICUserValueStructure.Capture_CcrValue_2); /*When the rising edge is captured for the second time, configure the capture edge as the falling edge to start a new round of capture*/ GPT_SetInputOperationMode(EXAMPLE_GPT,kGPT_InputCapture_Channel2,kGPT_InputOperation_RiseEdge); /*Start capture flag 0*/ GPT_ICUserValueStructure.Capture_StartFlag = 0; /*Capture completion flag set to 1 */ GPT_ICUserValueStructure.Capture_FinishFlag = 1; } } GPT_ClearStatusFlags(EXAMPLE_GPT, kGPT_InputCapture2Flag); } //tos_knl_irq_leave(); } void delay(uint32_t count) { volatile uint32_t i = 0; for (i = 0; i < count; ++i) { __asm("NOP"); } } void motor_ctrl_entry(void *arg) { /*Initialize and turn on GPT timer*/ GPT_Config(); SR04_TRIG_ON(); delay(2000);//20us SR04_TRIG_OFF(); while (1) { if (car_cmd.onoff){ USER_LED_ON(); }else{ USER_LED_OFF(); } if ((car_cmd.drive)&&(distance>=300)){ MOTOR_DRIVE_EN(); }else if(car_cmd.reverse){ MOTOR_REVERSE_EN(); }else if(car_cmd.right){ MOTOR_RIGHT_EN(); }else if(car_cmd.left){ MOTOR_LEFT_EN(); }else{ MOTOR_STOP(); } trig_cnt++; if(GPT_ICUserValueStructure.Capture_FinishFlag) { /*Get the count value. The timer is 64 bit data, and 32 bits are likely to overflow*/ timer = GPT_ICUserValueStructure.Capture_Period * 0xffffffff; timer += GPT_ICUserValueStructure.Capture_CcrValue_2; timer -= GPT_ICUserValueStructure.Capture_CcrValue_1; /*Convert count value to time, unit (ms)*/ //timer = timer / ((EXAMPLE_GPT_CLK_FREQ)/1000); // Unit ms timer = (172*timer) / ((EXAMPLE_GPT_CLK_FREQ)/1000);//One way sound velocity 344 / 2 = 172 distance unit mm distance =(int)(timer); //PRINTF("the result is: %lld ms \r\n",timer); PRINTF("the result is: %lld ms \r\n",distance); GPT_ICUserValueStructure.Capture_FinishFlag = 0; SR04_TRIG_ON(); delay(2000);//20us SR04_TRIG_OFF(); trig_cnt = 0; } if(trig_cnt>10) { trig_cnt = 0; SR04_TRIG_ON(); delay(2000);//20us SR04_TRIG_OFF(); } //PRINTF("###I am task1 %d \r\n",GPT_ICUserValueStructure.Capture_Period); tos_task_delay(20); } }
6, Tencent Lianlian H5 panel.
1. Computer debugging interface.
2. Mobile interface.
The standard panel is used here, and the use method is still very simple, but the interface and functions need to be optimized. If you have time to use SDK for development, it should be very good.
7, Summary.
In the past, I had little contact with projects related to the Internet of things. With the popularization and application of the Internet of things, I may use it a lot in my future work. Through this project, I really learned a lot of knowledge and benefited a lot. Follow the tutorial and transplant TencentOS Tiny kernel. It's only a few steps, very simple. Tencent cloud IoT explorer platform is used for the first time. It uses H5 standard panel and gets started very quickly. NXP MCU has been used in work, which can be said to be very emotional. If you can get a RT1062 development board, you will be infinitely satisfied. Ha ha.
MCUXpresso IDE, the official development tool of NXP, is used to keil. It's not easy to use at the beginning, but it's easy to use and has very powerful functions. The most important thing is that keil needs copyright. The official can provide free IDE, so it really needs more support.
Because time is too tight, many functions have not been done. Later, we will continue to improve when we have time and continue to learn TencentOS Tiny and RT1062.