/dev/block/bootdevice/by-name/system /system ext4 ro,barrier=1,discard wait,avb=vbmeta_system,slotselect,logical,first_stage_mount,avb_keys=/avb/qcar-gsi.avbpubkey

时间: 2024-02-14 19:24:10 浏览: 175
这看起来像是一个 Android 系统的启动脚本,其中 `/dev/block/bootdevice/by-name/system` 是系统分区的设备文件路径,`ext4` 是文件系统类型,`ro` 表示只读,`barrier=1` 表示开启数据同步写入,`discard` 表示开启闪存 TRIM,`wait,avb=vbmeta_system,slotselect,logical,first_stage_mount,avb_keys=/avb/qcar-gsi.avbpubkey` 表示等待系统分区挂载完成,并通过 AVB 验证引导映像的完整性。
相关问题

/dev/block/bootdevice/by-name/system / ext4 ro,barrier=1,discard wait,slotselect,avb,avb_keys=/avb/qcar-gsi.avbpubkey

这是一个fstab文件中的一行描述,其作用是挂载/system分区。 具体来说,这行描述的含义如下: - /dev/block/bootdevice/by-name/system:表示系统中的设备文件,用于表示系统分区的位置。在这里,它表示系统分区所在的设备,即通过引导设备名称来访问的设备。 - /:表示文件系统的挂载点,即将系统分区挂载到根目录下。 - ext4:表示文件系统的类型,即系统分区使用的文件系统类型。 - ro,barrier=1,discard:表示挂载选项,其中ro表示文件系统只读,barrier=1表示启用数据同步,discard表示支持TRIM命令。 - wait,slotselect,avb,avb_keys=/avb/qcar-gsi.avbpubkey:表示mount选项,其中wait表示等待设备就绪后再挂载,slotselect表示选择槽位,avb表示Android Verified Boot,avb_keys表示AVB公钥的路径。 总的来说,这行描述的作用是将系统分区挂载到根目录下,并使用只读模式、启用数据同步和支持TRIM命令等挂载选项,同时还使用了一些mount选项来确保系统能够正确启动。

这是一个针对 rk3288 平台的 Android 系统的 fstab 文件,它定义了系统中各个分区的挂载点、文件系统类型和挂载选项。文件名中的 forceencrypt.bootmode.emmc 暗示了该系统启用了强制加密功能,并且使用了 eMMC 存储设备。以下是该文件的内容: 复制 # Android fstab file for rk3288 emmc devices # # The filesystem that contains the filesystem checker binary (typically /system) cannot # specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK # mount point fstype device [device2] [options] [flags] # boot, recovery, and cache are automatically set up with defaults /dev/block/platform/ff0f0000.dwmmc/by-name/system /system ext4 ro,barrier=1,discard wait /dev/block/platform/ff0f0000.dwmmc/by-name/userdata /data ext4 noatime,nodiratime,nosuid,nodev,noauto_da_alloc,discard,errors=panic wait,check,encryptable=footer # The following entry is optional, but can be used to specify an # alternate resource file to be used instead of the compiled-in # defaults: # /system/etc/recovery.fstab # A/B OTA support /dev/block/platform/ff0f0000.dwmmc/by-name/system_other /system ext4 ro,barrier=1,discard wait,slotselect /dev/block/platform/ff0f0000.dwmmc/by-name/userdata_other /data ext4 noatime,nodiratime,nosuid,nodev,noauto_da_alloc,discard,errors=panic wait,check,encryptable=footer,slotselect # /cache partition is optional, wipe cache if needed /dev/block/platform/ff0f0000.dwmmc/by-name/cache /cache ext4 noatime,nodiratime,nosuid,nodev,noauto_da_alloc,discard wait,check # /vendor partition is optional, mount if present /dev/block/platform/ff0f0000.dwmmc/by-name/vendor /vendor ext4 ro,barrier=1,noauto_da_alloc wait # /misc partition is optional, mount if present /dev/block/platform/ff0f0000.dwmmc/by-name/misc /misc emmc defaults defaults # /metadata partition is optional, mount if present /dev/block/platform/ff0f0000.dwmmc/by-name/metadata /metadata ext4 noatime,nodiratime,nosuid,nodev,noauto_da_alloc,discard wait,check

非常感谢您提供的信息,现在我明白了您的问题。这是一个针对 rk3288 平台的 Android 系统的 fstab 文件,它定义了系统中各个分区的挂载点、文件系统类型和挂载选项。文件名中的 forceencrypt.bootmode.emmc 暗示了该系统启用了强制加密功能,并且使用了 eMMC 存储设备。具体来说,该文件指定了以下分区的挂载点和选项: - /system:用于存储系统文件,只读挂载,使用 ext4 文件系统,启用了屏障和 TRIM,等待其他分区挂载完毕后再挂载。 - /data:用于存储用户数据,使用 ext4 文件系统,启用了 noatime、nodiratime、nosuid、nodev、noauto_da_alloc、discard 和 errors=panic 选项,等待其他分区挂载完毕后再挂载,并且该分区支持加密。 - /system_other 和 /data_other:用于 A/B OTA 更新的备用分区,具有与 /system 和 /data 相同的挂载选项,但启用了 slotselect 选项。 - /cache:用于缓存数据,使用 ext4 文件系统,启用了 noatime、nodiratime、nosuid、nodev、noauto_da_alloc 和 discard 选项,等待其他分区挂载完毕后再挂载。 - /vendor:用于存储供应商提供的固件文件,只读挂载,使用 ext4 文件系统,启用了屏障和 noauto_da_alloc 选项,等待其他分区挂载完毕后再挂载。 - /misc:用于存储设备的一些杂项信息,使用 emmc 文件系统,使用默认选项挂载。 - /metadata:用于存储文件系统元数据,使用 ext4 文件系统,启用了 noatime、nodiratime、nosuid、nodev、noauto_da_alloc 和 discard 选项,等待其他分区挂载完毕后再挂载。 希望这个解释对您有所帮助!
阅读全文

相关推荐

/dev/block/dm-74 on /data type f2fs (rw,lazytime,seclabel,nosuid,nodev,noatime,background_gc=on,nogc_merge,discard,discard_unit=block,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,barrier,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,inlinecrypt,alloc_mode=default,checkpoint_merge,fsync_mode=nobarrier,compress_algorithm=lz4,compress_log_size=2,compress_mode=user,memory=normal) tmpfs on /data_mirror type tmpfs (rw,seclabel,nosuid,nodev,noexec,relatime,size=5766052k,nr_inodes=1441513,mode=700,gid=1000) /dev/block/dm-74 on /data_mirror/data_ce/null type f2fs (rw,lazytime,seclabel,nosuid,nodev,noatime,background_gc=on,nogc_merge,discard,discard_unit=block,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,barrier,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,inlinecrypt,alloc_mode=default,checkpoint_merge,fsync_mode=nobarrier,compress_algorithm=lz4,compress_log_size=2,compress_mode=user,memory=normal) /dev/block/dm-74 on /data_mirror/data_de/null type f2fs (rw,lazytime,seclabel,nosuid,nodev,noatime,background_gc=on,nogc_merge,discard,discard_unit=block,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,barrier,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,inlinecrypt,alloc_mode=default,checkpoint_merge,fsync_mode=nobarrier,compress_algorithm=lz4,compress_log_size=2,compress_mode=user,memory=normal) /dev/block/dm-74 on /data_mirror/misc_ce/null type f2fs (rw,lazytime,seclabel,nosuid,nodev,noatime,background_gc=on,nogc_merge,discard,discard_unit=block,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,barrier,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,inlinecrypt,alloc_mode=default,checkpoint_merge,fsync_mode=nobarrier,compress_algorithm=lz4,compress_log_size=2,compress_mode=user,memory=normal) /dev/block/dm-74 on /data_mirror/misc_de/null type f2fs (rw,lazytime,seclabel,nosuid,nodev,noatime,background_gc=on,nogc_merge,discard,discard_unit=block,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,barrier,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,inlinecrypt,alloc_mode=default,checkpoint_merge,fsync_mode=nobarrier,compress_algorithm=lz4,compress_log_size=2,compress_mode=user,memory=normal) /dev/block/dm-74 on /data_mirror/storage_area type f2fs (rw,lazytime,seclabel,nosuid,nodev,noatime,background_gc=on,nogc_merge,discard,discard_unit=block,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,barrier,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,inlinecrypt,alloc_mode=default,checkpoint_merge,fsync_mode=nobarrier,compress_algorithm=lz4,compress_log_size=2,compress_mode=user,memory=normal) /dev/block/dm-74 on /data_mirror/cur_profiles type f2fs (rw,lazytime,seclabel,nosuid,nodev,noatime,background_gc=on,nogc_merge,discard,discard_unit=block,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,barrier,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,inlinecrypt,alloc_mode=default,checkpoint_merge,fsync_mode=nobarrier,compress_algorithm=lz4,compress_log_size=2,compress_mode=user,memory=normal) /dev/block/dm-74 on /data_mirror/ref_profiles type f2fs (rw,lazytime,seclabel,nosuid,nodev,noatime,background_gc=on,nogc_merge,discard,discard_unit=block,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,barrier,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,inlinecrypt,alloc_mode=default,checkpoint_merge,fsync_mode=nobarrier,compress_algorithm=lz4,compress_log_size=2,compress_mode=user,memory=normal) /dev/block/dm-74 on /data_mirror/data_ce/null/0 type f2fs (rw,lazytime,seclabel,nosuid,nodev,noatime,background_gc=on,nogc_merge,discard,discard_unit=block,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,barrier,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,inlinecrypt,alloc_mode=default,checkpoint_merge,fsync_mode=nobarrier,compress_algorithm=lz4,compress_log_size=2,compress_mode=user,memory=normal) /dev/block/dm-74 on /data/user/0 type f2fs (rw,lazytime,seclabel,nosuid,nodev,noatime,background_gc=on,nogc_merge,discard,discard_unit=block,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,barrier,extent_cache,mode=adaptive,active_logs=6,reserve_root=32768,resuid=0,resgid=1065,inlinecrypt,alloc_mode=default,checkpoint_merge,fsync_mode=nobarrier,compress_algorithm=lz4,compress_log_size=2,compress_mode=user,memory=normal) /dev/block/sdf3 on /data/persist_log/oplusreserve/media/log/shutdown type ext4 (rw,seclabel,nosuid,nodev,noatime) /dev/block/sdf3 on /data/persist_log/oplusreserve/media/log/hang_oplus type ext4 (rw,seclabel,nosuid,nodev,noatime) /dev/block/sdf3 on /data/persist_log/cache/factory type ext4 (rw,seclabel,nosuid,nodev,noatime) /dev/block/sdf3 on /data/persist_log/oplus_fsck_fulldiskscanuserdata type ext4 (rw,seclabel,nosuid,nodev,noatime) /dev/block/sdf3 on /data/persist_log/criticallog type ext4 (rw,seclabel,nosuid,nodev,noatime) /dev/block/sdf3 on /data/persist_log/oplusreserve/media/log/minidumpbackup type ext4 (rw,seclabel,nosuid,nodev,noatime) /dev/block/sdf3 on /data/persist_log/storage/op2storagelog type ext4 (rw,seclabel,nosuid,nodev,noatime)并没有/dev/block/bootdevice/by-name/userdata 是什么原因

Using: /home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/staging_dir/toolchain-riscv64_c908_gcc-10.4.0-thead2.10.2_musl/include/asm/auxvec.h Alternative: "/home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/sf_kernel/linux-5.10/arch/riscv/include/uapi" Using: /home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/build_dir/target-riscv64_c908_musl/linux-siflower_sf21a6826/backports-5.15.162-1/backport-include/asm/barrier.h Alternative: "/home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/sf_kernel/linux-5.10/arch/riscv/include" Using: /home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/staging_dir/toolchain-riscv64_c908_gcc-10.4.0-thead2.10.2_musl/include/asm/errno.h Alternative: "/home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/sf_kernel/linux-5.10/arch/riscv/include/generated/uapi" Using: /home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/build_dir/target-riscv64_c908_musl/linux-siflower_sf21a6826/backports-5.15.162-1/backport-include/asm/unaligned.h Alternative: "/home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/sf_kernel/linux-5.10/arch/riscv/include/generated" Using: /home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/build_dir/target-riscv64_c908_musl/linux-siflower_sf21a6826/sf_wifi-1.0/umac/hal_desc.h Alternative: "/home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/package/kernel/siflower/openwrt-x2880-driver/src/umac" Alternative: "/home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/build_dir/target-riscv64_c908_musl/linux-siflower_sf21a6826/sf_wifi-1.0/fmac/umac" Using: /home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/build_dir/target-riscv64_c908_musl/linux-siflower_sf21a6826/sf_wifi-1.0/umac/fullmac/siwifi_defs.h Alternative: "/home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/build_dir/target-riscv64_c908_musl/linux-siflower_sf21a6826/sf_wifi-1.0/fmac/umac/fullmac" Alternative: "/home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/package/kernel/siflower/openwrt-x2880-driver/src/umac/fullmac" Using: /home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/staging_dir/toolchain-riscv64_c908_gcc-10.4.0-thead2.10.2_musl/include/string.h Alternative: "/home/jianan.zhao/backups/code/px_pagepool/Openwrt-master/sf_kernel/linux-5.10/arch/riscv/include/asm"

#include <cstdlib> #include <dlfcn.h> #include "VulkanGraphics.h" #include "imgui_impl_vulkan.h" #include <vulkan/vulkan_android.h> #include <android/native_window.h> #include <unistd.h> #ifndef NDEBUG static void check_vk_result(VkResult err) { if (err == 0) return; fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); if (err < 0) abort(); } #else static void check_vk_result(VkResult err) { } #endif VkPhysicalDevice VulkanGraphics::SetupVulkan_SelectPhysicalDevice() { uint32_t gpu_count; VkResult err = vkEnumeratePhysicalDevices(m_Instance, &gpu_count, nullptr); check_vk_result(err); IM_ASSERT(gpu_count > 0); ImVector<VkPhysicalDevice> gpus; gpus.resize(gpu_count); err = vkEnumeratePhysicalDevices(m_Instance, &gpu_count, gpus.Data); check_vk_result(err); // If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers // most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple // dedicated GPUs) is out of scope of this sample. for (VkPhysicalDevice &device: gpus) { VkPhysicalDeviceProperties properties; vkGetPhysicalDeviceProperties(device, &properties); if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) return device; } // Use first GPU (Integrated) is a Discrete one is not available. if (gpu_count > 0) return gpus[0]; return VK_NULL_HANDLE; } bool VulkanGraphics::Create(ANativeWindow *Window, int m_Width, int m_Height) { this->m_Window = Window; if (InitVulkan() != 1) { fprintf(stderr, "Vulkan is not supported %s\n", dlerror()); abort(); } wd = std::make_unique<ImGui_ImplVulkanH_Window>(); //为imgui加载vulkan函数 void *libvulkan = dlopen("libvulkan.so", RTLD_NOW); ImGui_ImplVulkan_LoadFunctions([](const char *function_name, void *handle) -> PFN_vkVoidFunction { return reinterpret_cast(dlsym(handle, function_name)); }, libvulkan); VkResult err; // Create Vulkan Instance { const char *instance_extensions[] = { "VK_KHR_surface", "VK_KHR_android_surface", }; VkApplicationInfo appInfo = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pNext = nullptr, .pApplicationName = "pApplicationName", .applicationVersion = VK_MAKE_VERSION(1, 0, 0), .pEngineName = "pEngineName", .engineVersion = VK_MAKE_VERSION(1, 0, 0), .apiVersion = VK_MAKE_VERSION(1, 1, 0), }; VkInstanceCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; create_info.pApplicationInfo = &appInfo; create_info.enabledExtensionCount = sizeof(instance_extensions) / sizeof(instance_extensions[0]); create_info.ppEnabledExtensionNames = instance_extensions; #ifdef IMGUI_VULKAN_DEBUG_REPORT // Enabling validation layers const char* layers[] = { "VK_LAYER_KHRONOS_validation" }; create_info.enabledLayerCount = 1; create_info.ppEnabledLayerNames = layers; // Enable debug report extension (we need additional storage, so we duplicate the user array to add our new extension to it) const char** extensions_ext = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); memcpy(extensions_ext, extensions, extensions_count * sizeof(const char*)); extensions_ext[extensions_count] = "VK_EXT_debug_report"; create_info.enabledExtensionCount = extensions_count + 1; create_info.ppEnabledExtensionNames = extensions_ext; // Create Vulkan Instance err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); check_vk_result(err); free(extensions_ext); // Get the function pointer (required for any extensions) auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); IM_ASSERT(vkCreateDebugReportCallbackEXT != NULL); // Setup the debug report callback VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; debug_report_ci.pfnCallback = debug_report; debug_report_ci.pUserData = NULL; err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); check_vk_result(err); #else // Create Vulkan Instance without any debug feature err = vkCreateInstance(&create_info, m_Allocator, &m_Instance); check_vk_result(err); IM_UNUSED(m_DebugReport); #endif } // Select Physical Device (GPU) m_PhysicalDevice = SetupVulkan_SelectPhysicalDevice(); // Select graphics queue family { uint32_t count; vkGetPhysicalDeviceQueueFamilyProperties(m_PhysicalDevice, &count, nullptr); VkQueueFamilyProperties *queues = (VkQueueFamilyProperties *) malloc(sizeof(VkQueueFamilyProperties) * count); vkGetPhysicalDeviceQueueFamilyProperties(m_PhysicalDevice, &count, queues); for (uint32_t i = 0; i < count; i++) if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { m_QueueFamily = i; break; } free(queues); IM_ASSERT(m_QueueFamily != (uint32_t) -1); } // Create Logical Device (with 1 queue) { ImVector<const char *> device_extensions; device_extensions.push_back("VK_KHR_swapchain"); // Enumerate physical device extension uint32_t properties_count; ImVector<VkExtensionProperties> properties; vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &properties_count, nullptr); properties.resize(properties_count); vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &properties_count, properties.Data); #ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); #endif const float queue_priority[] = {1.0f}; VkDeviceQueueCreateInfo queue_info[1] = {}; queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_info[0].queueFamilyIndex = m_QueueFamily; queue_info[0].queueCount = 1; queue_info[0].pQueuePriorities = queue_priority; VkDeviceCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]); create_info.pQueueCreateInfos = queue_info; create_info.enabledExtensionCount = (uint32_t) device_extensions.Size; create_info.ppEnabledExtensionNames = device_extensions.Data; err = vkCreateDevice(m_PhysicalDevice, &create_info, m_Allocator, &m_Device); check_vk_result(err); vkGetDeviceQueue(m_Device, m_QueueFamily, 0, &m_Queue); } // Create Descriptor Pool { VkDescriptorPoolSize pool_sizes[] = { {VK_DESCRIPTOR_TYPE_SAMPLER, 1000}, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000}, {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000}, {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000}, {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000}, {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000}, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000}, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000}, {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000} }; VkDescriptorPoolCreateInfo pool_info = {}; pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes); pool_info.poolSizeCount = (uint32_t) IM_ARRAYSIZE(pool_sizes); pool_info.pPoolSizes = pool_sizes; err = vkCreateDescriptorPool(m_Device, &pool_info, m_Allocator, &m_DescriptorPool); check_vk_result(err); } { // Create Window Surface VkSurfaceKHR surface; VkAndroidSurfaceCreateInfoKHR createInfo{ .sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, .pNext = nullptr, .flags = 0, .window = m_Window}; err = vkCreateAndroidSurfaceKHR(m_Instance, &createInfo, m_Allocator, &surface); check_vk_result(err); wd->Surface = surface; // Check for WSI support VkBool32 res; vkGetPhysicalDeviceSurfaceSupportKHR(m_PhysicalDevice, m_QueueFamily, wd->Surface, &res); if (res != VK_TRUE) { fprintf(stderr, "Error no WSI support on physical device 0\n"); exit(-1); } // Select Surface Format const VkFormat requestSurfaceImageFormat[] = {VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM}; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(m_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t) IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); // 呈现模式 // 代码中已有逻辑,无需修改,只需确保不定义APP_USE_UNLIMITED_FRAME_RATE #ifndef APP_USE_UNLIMITED_FRAME_RATE VkPresentModeKHR present_modes[] = {VK_PRESENT_MODE_FIFO_KHR}; // 垂直同步模式 #endif wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(m_PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes)); //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode); // Create SwapChain, RenderPass, Framebuffer, etc. IM_ASSERT(m_MinImageCount >= 2); // 在调用 ImGui_ImplVulkanH_CreateOrResizeWindow 前添加 m_MinImageCount = 2; // 双缓冲,或根据屏幕刷新率设置为3(三缓冲) ImGui_ImplVulkanH_CreateOrResizeWindow(m_Instance, m_PhysicalDevice, m_Device, wd.get(), m_QueueFamily, m_Allocator, (int) m_Width, (int) m_Height, m_MinImageCount); } return true; } void VulkanGraphics::Setup() { ImGui_ImplVulkan_InitInfo init_info = {}; init_info.Instance = m_Instance; init_info.PhysicalDevice = m_PhysicalDevice; init_info.Device = m_Device; init_info.QueueFamily = m_QueueFamily; init_info.Queue = m_Queue; init_info.PipelineCache = m_PipelineCache; init_info.DescriptorPool = m_DescriptorPool; init_info.RenderPass = wd->RenderPass; init_info.Subpass = 0; init_info.MinImageCount = m_MinImageCount; init_info.ImageCount = wd->ImageCount; init_info.MSAASamples = VK_SAMPLE_COUNT_2_BIT; init_info.Allocator = m_Allocator; init_info.CheckVkResultFn = check_vk_result; ImGui_ImplVulkan_Init(&init_info); } void VulkanGraphics::PrepareFrame(bool resize) { if (m_SwapChainRebuild || resize) { int width = ANativeWindow_getWidth(m_Window); int height = ANativeWindow_getHeight(m_Window); if (m_LastWidth == 0 || m_LastHeight == 0) { m_LastWidth = width; m_LastHeight = height; } if (width > 0 && height > 0) { if (width != m_LastWidth || height != m_LastHeight) { //切屏休息半秒 usleep(500000); m_LastWidth = width; m_LastHeight = height; ImGui_ImplVulkan_SetMinImageCount(m_MinImageCount); ImGui_ImplVulkanH_CreateOrResizeWindow(m_Instance, m_PhysicalDevice, m_Device, wd.get(), m_QueueFamily, m_Allocator, width, height, m_MinImageCount); wd->FrameIndex = 0; m_SwapChainRebuild = false; } } } ImGui_ImplVulkan_NewFrame(); } void VulkanGraphics::Render(ImDrawData *drawData) { VkResult err; VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; err = vkAcquireNextImageKHR(m_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); if (err == VK_ERROR_OUT_OF_DATE_KHR /*|| err == VK_SUBOPTIMAL_KHR*/) { m_SwapChainRebuild = true; return; } //check_vk_result(err); ImGui_ImplVulkanH_Frame *fd = &wd->Frames[wd->FrameIndex]; { err = vkWaitForFences(m_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking check_vk_result(err); err = vkResetFences(m_Device, 1, &fd->Fence); check_vk_result(err); } { err = vkResetCommandPool(m_Device, fd->CommandPool, 0); check_vk_result(err); VkCommandBufferBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; err = vkBeginCommandBuffer(fd->CommandBuffer, &info); check_vk_result(err); } { //透明 默认已经是0了 //memset(wd->ClearValue.color.float32, 0, sizeof(wd->ClearValue.color.float32)); VkRenderPassBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; info.renderPass = wd->RenderPass; info.framebuffer = fd->Framebuffer; info.renderArea.extent.width = wd->Width; info.renderArea.extent.height = wd->Height; info.clearValueCount = 1; info.pClearValues = &wd->ClearValue; vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); } // Record dear imgui primitives into command buffer ImGui_ImplVulkan_RenderDrawData(drawData, fd->CommandBuffer); // Submit command buffer vkCmdEndRenderPass(fd->CommandBuffer); { VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; info.waitSemaphoreCount = 1; info.pWaitSemaphores = &image_acquired_semaphore; info.pWaitDstStageMask = &wait_stage; info.commandBufferCount = 1; info.pCommandBuffers = &fd->CommandBuffer; info.signalSemaphoreCount = 1; info.pSignalSemaphores = &render_complete_semaphore; err = vkEndCommandBuffer(fd->CommandBuffer); check_vk_result(err); err = vkQueueSubmit(m_Queue, 1, &info, fd->Fence); check_vk_result(err); } { if (m_SwapChainRebuild) return; VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; info.pWaitSemaphores = &render_complete_semaphore; info.swapchainCount = 1; info.pSwapchains = &wd->Swapchain; info.pImageIndices = &wd->FrameIndex; VkResult err = vkQueuePresentKHR(m_Queue, &info); if (err == VK_ERROR_OUT_OF_DATE_KHR /*|| err == VK_SUBOPTIMAL_KHR*/) { m_SwapChainRebuild = true; return; } //check_vk_result(err); wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores } } void VulkanGraphics::PrepareShutdown() { VkResult err = vkDeviceWaitIdle(m_Device); check_vk_result(err); ImGui_ImplVulkan_Shutdown(); } //==========// void VulkanGraphics::Cleanup() { ImGui_ImplVulkanH_DestroyWindow(m_Instance, m_Device, wd.get(), m_Allocator); vkDestroyDescriptorPool(m_Device, m_DescriptorPool, m_Allocator); #ifdef APP_USE_VULKAN_DEBUG_REPORT // Remove the debug report callback auto vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT"); vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator); #endif // APP_USE_VULKAN_DEBUG_REPORT vkDestroyDevice(m_Device, m_Allocator); vkDestroyInstance(m_Instance, m_Allocator); } //清理释放 // Helper function to find Vulkan memory type bits. See ImGui_ImplVulkan_MemoryType() in imgui_impl_vulkan.cpp uint32_t VulkanGraphics::findMemoryType(uint32_t type_filter, VkMemoryPropertyFlags properties) { VkPhysicalDeviceMemoryProperties mem_properties; vkGetPhysicalDeviceMemoryProperties(m_PhysicalDevice, &mem_properties); for (uint32_t i = 0; i < mem_properties.memoryTypeCount; i++) if ((type_filter & (1 << i)) && (mem_properties.memoryTypes[i].propertyFlags & properties) == properties) return i; return 0xFFFFFFFF; // Unable to find memoryType } BaseTexData *VulkanGraphics::LoadTexture(BaseTexData *tex, void *pixel_data) { auto *tex_data = new VulkanTextureData(); tex_data->Width = tex->Width; tex_data->Height = tex->Height; tex_data->Channels = tex->Channels; // tex_data->UserData= // Calculate allocation size (in number of bytes) size_t image_size = tex_data->Width * tex_data->Height * tex_data->Channels; VkResult err; // Create the Vulkan image. { VkImageCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; info.imageType = VK_IMAGE_TYPE_2D; info.format = VK_FORMAT_R8G8B8A8_UNORM; info.extent.width = tex_data->Width; info.extent.height = tex_data->Height; info.extent.depth = 1; info.mipLevels = 1; info.arrayLayers = 1; info.samples = VK_SAMPLE_COUNT_2_BIT; info.tiling = VK_IMAGE_TILING_OPTIMAL; info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; err = vkCreateImage(m_Device, &info, m_Allocator, &tex_data->Image); check_vk_result(err); VkMemoryRequirements req; vkGetImageMemoryRequirements(m_Device, tex_data->Image, &req); VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = findMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); err = vkAllocateMemory(m_Device, &alloc_info, m_Allocator, &tex_data->ImageMemory); check_vk_result(err); err = vkBindImageMemory(m_Device, tex_data->Image, tex_data->ImageMemory, 0); check_vk_result(err); } // Create the Image View { VkImageViewCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; info.image = tex_data->Image; info.viewType = VK_IMAGE_VIEW_TYPE_2D; info.format = VK_FORMAT_R8G8B8A8_UNORM; info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; info.subresourceRange.levelCount = 1; info.subresourceRange.layerCount = 1; err = vkCreateImageView(m_Device, &info, m_Allocator, &tex_data->ImageView); check_vk_result(err); } // Create Sampler { VkSamplerCreateInfo sampler_info{}; sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; sampler_info.magFilter = VK_FILTER_LINEAR; sampler_info.minFilter = VK_FILTER_LINEAR; sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; // outside image bounds just use border color sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; sampler_info.minLod = -1000; sampler_info.maxLod = 1000; sampler_info.maxAnisotropy = 1.0f; err = vkCreateSampler(m_Device, &sampler_info, m_Allocator, &tex_data->Sampler); check_vk_result(err); } // Create Descriptor Set using ImGUI's implementation tex_data->DS = (void *) ImGui_ImplVulkan_AddTexture(tex_data->Sampler, tex_data->ImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); // Create Upload Buffer { VkBufferCreateInfo buffer_info = {}; buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_info.size = image_size; buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; err = vkCreateBuffer(m_Device, &buffer_info, m_Allocator, &tex_data->UploadBuffer); check_vk_result(err); VkMemoryRequirements req; vkGetBufferMemoryRequirements(m_Device, tex_data->UploadBuffer, &req); VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = findMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); err = vkAllocateMemory(m_Device, &alloc_info, m_Allocator, &tex_data->UploadBufferMemory); check_vk_result(err); err = vkBindBufferMemory(m_Device, tex_data->UploadBuffer, tex_data->UploadBufferMemory, 0); check_vk_result(err); } // Upload to Buffer: { void *map = NULL; err = vkMapMemory(m_Device, tex_data->UploadBufferMemory, 0, image_size, 0, &map); check_vk_result(err); memcpy(map, pixel_data, image_size); VkMappedMemoryRange range[1] = {}; range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range[0].memory = tex_data->UploadBufferMemory; range[0].size = image_size; err = vkFlushMappedMemoryRanges(m_Device, 1, range); check_vk_result(err); vkUnmapMemory(m_Device, tex_data->UploadBufferMemory); } // Create a command buffer that will perform following steps when hit in the command queue. // TODO: this works in the example, but may need input if this is an acceptable way to access the pool/create the command buffer. VkCommandPool command_pool = wd->Frames[wd->FrameIndex].CommandPool; VkCommandBuffer command_buffer; { VkCommandBufferAllocateInfo alloc_info{}; alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; alloc_info.commandPool = command_pool; alloc_info.commandBufferCount = 1; err = vkAllocateCommandBuffers(m_Device, &alloc_info, &command_buffer); check_vk_result(err); VkCommandBufferBeginInfo begin_info = {}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; err = vkBeginCommandBuffer(command_buffer, &begin_info); check_vk_result(err); } // Copy to Image { VkImageMemoryBarrier copy_barrier[1] = {}; copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; copy_barrier[0].image = tex_data->Image; copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copy_barrier[0].subresourceRange.levelCount = 1; copy_barrier[0].subresourceRange.layerCount = 1; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, copy_barrier); VkBufferImageCopy region = {}; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.layerCount = 1; region.imageExtent.width = tex_data->Width; region.imageExtent.height = tex_data->Height; region.imageExtent.depth = 1; vkCmdCopyBufferToImage(command_buffer, tex_data->UploadBuffer, tex_data->Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); VkImageMemoryBarrier use_barrier[1] = {}; use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; use_barrier[0].image = tex_data->Image; use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; use_barrier[0].subresourceRange.levelCount = 1; use_barrier[0].subresourceRange.layerCount = 1; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, use_barrier); } // End command buffer { VkSubmitInfo end_info = {}; end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; end_info.commandBufferCount = 1; end_info.pCommandBuffers = &command_buffer; err = vkEndCommandBuffer(command_buffer); check_vk_result(err); err = vkQueueSubmit(m_Queue, 1, &end_info, VK_NULL_HANDLE); check_vk_result(err); err = vkDeviceWaitIdle(m_Device); check_vk_result(err); } return tex_data; } void VulkanGraphics::RemoveTexture(BaseTexData *tex) { auto *tex_data = (VulkanTextureData *) (tex); vkFreeMemory(m_Device, tex_data->UploadBufferMemory, nullptr); vkDestroyBuffer(m_Device, tex_data->UploadBuffer, nullptr); vkDestroySampler(m_Device, tex_data->Sampler, nullptr); vkDestroyImageView(m_Device, tex_data->ImageView, nullptr); vkDestroyImage(m_Device, tex_data->Image, nullptr); vkFreeMemory(m_Device, tex_data->ImageMemory, nullptr); ImGui_ImplVulkan_RemoveTexture((VkDescriptorSet) tex_data->DS); delete tex_data; } TextureInfo VulkanGraphics::ImAgeHeadFile(const unsigned char *buf, int len) { TextureInfo ret_data{0}; BaseTexData tex_data{}; tex_data.Channels = 4; //unsigned char *image_data = loadFunc(&tex_data); unsigned char *image_data = stbi_load_from_memory((const stbi_uc *) buf, len, &tex_data.Width, &tex_data.Height, nullptr, tex_data.Channels); if (image_data == nullptr) return ret_data; auto result = LoadTexture(&tex_data, image_data); stbi_image_free(image_data); ret_data.textureId = (ImTextureID)result->DS; ret_data.w = tex_data.Width; ret_data.h = tex_data.Height; return ret_data; } 怎么改成 3 合缓冲

namespace fs_mgr { 32 33 struct FstabEntry { 34 std::string blk_device; 35 std::vector<std::string> user_devices; 36 std::string logical_partition_name; 37 std::string mount_point; 38 std::string fs_type; 39 unsigned long flags = 0; 40 std::string fs_options; 41 std::string fs_checkpoint_opts; 42 std::string metadata_key_dir; 43 std::string metadata_encryption_options; 44 off64_t length = 0; 45 std::string label; 46 int partnum = -1; 47 int swap_prio = -1; 48 int max_comp_streams = 0; 49 off64_t zram_size = 0; 50 off64_t reserved_size = 0; 51 off64_t readahead_size_kb = -1; 52 std::string encryption_options; 53 off64_t erase_blk_size = 0; 54 off64_t logical_blk_size = 0; 55 std::string sysfs_path; 56 std::string vbmeta_partition; 57 uint64_t zram_backingdev_size = 0; 58 std::string avb_keys; 59 std::string lowerdir; 60 std::string avb_hashtree_digest; 61 62 struct FsMgrFlags { 63 bool wait : 1; 64 bool check : 1; 65 bool crypt : 1; // Now only used to identify adoptable storage volumes 66 bool nonremovable : 1; 67 bool vold_managed : 1; 68 bool recovery_only : 1; 69 bool no_emulated_sd : 1; // No emulated sdcard daemon; sd card is the only external 70 // storage. 71 bool no_trim : 1; 72 bool file_encryption : 1; 73 bool formattable : 1; 74 bool slot_select : 1; 75 bool late_mount : 1; 76 bool no_fail : 1; 77 bool quota : 1; 78 bool avb : 1; 79 bool logical : 1; 80 bool checkpoint_blk : 1; 81 bool checkpoint_fs : 1; 82 bool first_stage_mount : 1; 83 bool slot_select_other : 1; 84 bool fs_verity : 1; 85 bool ext_meta_csum : 1; 86 bool fs_compress : 1; 87 bool overlayfs_remove_missing_lowerdir : 1; 88 /*feature-resize-v001-1-begin*/ 89 #ifdef OPLUS_FEATURE_STORAGE_RESIZE 90 //#[email protected], 2019/04/19, Add for android Q support resize 91 bool resize : 1; 92 #endif/*OPLUS_FEATURE_STORAGE*/ 93 /*feature-resize-v001-1-end*/ 94 95 #ifdef QCOM_ONLY_CHANGES 96 //add for metadata wrapped key support 97 bool wrapped_key : 1; 98 #endif /* QCOM_ONLY_CHANGES */ 99 bool is_zoned : 1; 100 } fs_mgr_flags = {}; 101 102 bool is_encryptable() const { return fs_mgr_flags.crypt; } 103 };

#include “stm32f10x.h” #include “delay.h” #include “motor.h” #include “keysacn.h” #include “IRSEARCH.h” #include “IRAvoid.h” /* void ZYSTM32_run(int speed,int time); //前进函数 void ZYSTM32_brake(int time); //刹车函数 void ZYSTM32_Left(int speed,int time); //左转函数 void ZYSTM32_Spin_Left(int speed,int time); //左旋转函数 void ZYSTM32_Right(int speed,int time); //右转函数 void ZYSTM32_Spin_Right(int speed,int time);//右旋转函数 void ZYSTM32_back(int speed,int time); //后退函数 */ int main(void) { delay_init(); KEY_Init(); IRSearchInit(); IRAvoidInit(); TIM4_PWM_Init(7199,0); //初始化PWM ZYSTM32_brake(500); keysacn(); while(1) { if(AVOID_IO == BARRIER_Y ) { BEEP_SET; LED_D3_SET; ZYSTM32_back(50,200); ZYSTM32_Spin_Left(50,350); delay_ms(300); } else { BEEP_RESET; LED_D3_RESET; SearchRun(); //循迹函数 } } } #include “IRSEARCH.h” #include “delay.h” #include “motor.h” #include “stm32f10x.h” // Device header char ctrl_comm = COMM_STOP;//控制指令 char ctrl_comm_last = COMM_STOP;//上一次的指令 void IRSearchInit(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB , ENABLE); GPIO_InitStructure.GPIO_Pin = SEARCH_R_PIN;//配置使能GPIO管脚 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//配置GPIO模式,输入上拉 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//配置GPIO端口速度 GPIO_Init(SEARCH_R_GPIO , &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = SEARCH_L_PIN;//配置使能GPIO管脚 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//配置GPIO模式,输入上拉 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//配置GPIO端口速度 GPIO_Init(SEARCH_L_GPIO , &GPIO_InitStructure); } void SearchRun(void) { if(SEARCH_L_IO == WHITE_AREA && SEARCH_R_IO == WHITE_AREA) ctrl_comm = COMM_UP; else if (SEARCH_L_IO == BLACK_AREA && SEARCH_R_IO == WHITE_AREA) ctrl_comm = COMM_RIGHT; else if (SEARCH_R_IO == BLACK_AREA & SEARCH_L_IO == WHITE_AREA) ctrl_comm = COMM_LEFT; else ctrl_comm = COMM_DOWN; if(ctrl_comm_last != ctrl_comm) { ctrl_comm_last = ctrl_comm; switch(ctrl_comm) { case COMM_UP: ZYSTM32_run(80,6);break; case COMM_DOWN: ZYSTM32_back(50,100);ZYSTM32_Spin_Left(70,400);break; case COMM_LEFT: ZYSTM32_back(50,100);ZYSTM32_Spin_Left(70,400);break; case COMM_RIGHT: ZYSTM32_back(50,100);ZYSTM32_Spin_Right(70,250);break; case COMM_STOP: ZYSTM32_brake(10);break; default : break; } } } #include “IRAvoid.h” #include “delay.h” #include “motor.h” #include “keysacn.h” #include “stm32f10x.h” // Device header void IRAvoidInit(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); GPIO_InitStructure.GPIO_Pin = AVOID_PIN;//配置使能GPIO管脚 PB1 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//配置GPIO模式,输入上拉 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//配置GPIO端口速度 GPIO_Init(AVOID_PIN_GPIO , &GPIO_InitStructure); } void AVoidRun(void) { if(AVOID_IO == BARRIER_N ) { ZYSTM32_run(50,10); BEEP_RESET; LED_D3_RESET; } else { BEEP_SET; LED_D3_SET; ZYSTM32_brake(300);//停止300MS ZYSTM32_back(50,400);//后退400MS ZYSTM32_Left(50,500);//左转500MS } } #ifndef _IRAVOID_H #define _IRAVOID_H #include “stm32f10x.h” // Device header void IRAvoidInit(void); void AVoidRun(void); //避障传感器 /* 避障传感器 AVOID_PIN PB1 */ #define AVOID_PIN GPIO_Pin_1 #define AVOID_PIN_GPIO GPIOB #define AVOID_IO GPIO_ReadInputDataBit(AVOID_PIN_GPIO, AVOID_PIN) #define BARRIER_Y 0 //有障碍物 #define BARRIER_N 1 //无障碍物 #endif #include “motor.h” #include “Math.h” #include “delay.h” #include “stm32f10x.h” // Device header signed short sPWMR,sPWML,dPWM; //GPIO配置函数 /*void MotorGPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE); //使能PA,PB端口时钟 GPIO_InitStructure.GPIO_Pin = LEFT_MOTOR_GO; //左电机方向控制 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(LEFT_MOTOR_GO_GPIO, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = LEFT_MOTOR_PWM; GPIO_Init(LEFT_MOTOR_PWM_GPIO, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = RIGHT_MOTOR_PWM; GPIO_Init(RIGHT_MOTOR_PWM_GPIO, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = RIGHT_MOTOR_GO; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(RIGHT_MOTOR_GPIO, &GPIO_InitStructure); } void run() //前进 { RIGHT_MOTOR_GO_SET; RIGHT_MOTOR_PWM_RESET;//PB9 LEFT_MOTOR_GO_SET; LEFT_MOTOR_PWM_RESET;//PB8 } */ void TIM4_PWM_Init(unsigned short arr,unsigned short psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB , ENABLE); //使能GPIO外设时钟使能 GPIO_InitStructure.GPIO_Pin = LEFT_MOTOR_GO; //左电机方向控制 PB7 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(LEFT_MOTOR_GO_GPIO, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = LEFT_MOTOR_PWM; //左电机PWM控制 PB8 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(LEFT_MOTOR_PWM_GPIO, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = RIGHT_MOTOR_GO; //右电机方向控制 PA4 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(RIGHT_MOTOR_GPIO, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = RIGHT_MOTOR_PWM; //右电机PWM控制 PB9 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(RIGHT_MOTOR_PWM_GPIO, &GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 80K TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 不分频 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高 TIM_OC3Init(TIM4, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx TIM_OC4Init(TIM4, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx TIM_CtrlPWMOutputs(TIM4,ENABLE); //MOE 主输出使能 TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable); //CH1预装载使能 TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable); //CH1预装载使能 TIM_ARRPreloadConfig(TIM4, ENABLE); //使能TIMx在ARR上的预装载寄存器 TIM_Cmd(TIM4, ENABLE); //使能TIM1 } void SetMotorSpeed(unsigned char ucChannel,signed char cSpeed) { // static short sMotorSpeed = 0; short sPWM; // float fDir = 1; if (cSpeed>=100) cSpeed = 100; if (cSpeed<=-100) cSpeed = -100; sPWM = 7201 - fabs(cSpeed)*72; switch(ucChannel) { case 0://右轮 TIM_SetCompare3(TIM4,sPWM); if (cSpeed>0) RIGHT_MOTOR_GO_RESET; else if(cSpeed<0) RIGHT_MOTOR_GO_SET; break; case 1://左轮 TIM_SetCompare4(TIM4,sPWM); if (cSpeed>0) LEFT_MOTOR_GO_SET; else if (cSpeed<0) LEFT_MOTOR_GO_RESET; break; } } //----------------------------------运动函数-------------------------------- void ZYSTM32_run(signed char speed,int time) //前进函数 { signed char f_speed = - speed; SetMotorSpeed(1,f_speed);//左轮 //为负数 SetMotorSpeed(0,speed);//右轮 //为正数 delay_ms(time); //时间为毫秒 } void ZYSTM32_brake(int time) //刹车函数 { SetMotorSpeed(1,0);//左轮 //为0 SetMotorSpeed(0,0);//右轮 //为0 RIGHT_MOTOR_GO_RESET; LEFT_MOTOR_GO_RESET; delay_ms(time); //时间为毫秒 } void ZYSTM32_Left(signed char speed,int time) //左转函数 { SetMotorSpeed(1,0);//左轮 //左轮不动 SetMotorSpeed(0,speed); //右轮为正 delay_ms(time); //时间为毫秒 } void ZYSTM32_Spin_Left(signed char speed,int time) //左旋转函数 { SetMotorSpeed(1,speed);//左轮 //左轮为正 SetMotorSpeed(0,speed); //右轮为正 delay_ms(time); //时间为毫秒 } void ZYSTM32_Right(signed char speed,int time) //右转函数 { signed char f_speed = - speed; SetMotorSpeed(1,f_speed);//左轮 //左轮为负 SetMotorSpeed(0,0); //右轮为0 delay_ms(time); //时间为毫秒 } void ZYSTM32_Spin_Right(signed char speed,int time) //右旋转函数 { signed char f_speed = - speed; SetMotorSpeed(1,f_speed);//左轮 //左轮为负 SetMotorSpeed(0,f_speed); //右轮为负 delay_ms(time); //时间为毫秒 } void ZYSTM32_back(signed char speed,int time) //后退函数 { signed char u_speed = 100- speed; signed char f_speed = - u_speed; SetMotorSpeed(1,u_speed);//左轮 //为正数 SetMotorSpeed(0,f_speed);//右轮 //为负数 delay_ms(time); //时间为毫秒 } #include “delay.h” ////////////////////////////////////////////////////////////////////////////////// //如果需要使用OS,则包括下面的头文件即可. #if SYSTEM_SUPPORT_OS #include “includes.h” //ucos 使用 #endif ////////////////////////////////////////////////////////////////////////////////// //本程序只供学习使用,未经作者许可,不得用于其它任何用途 //ZYSTM32-A0机器人主控板 //使用SysTick的普通计数模式对延迟进行管理(适合STM32F10x系列) //包括delay_us,delay_ms //智宇电子 //www.hnzhiyu.cn //创建日期:2017/4/14 //版本:V1.8 //All rights reserved //******************************************************************************** ////////////////////////////////////////////////////////////////////////////////// static u8 fac_us=0; //us延时倍乘数 static u16 fac_ms=0; //ms延时倍乘数,在ucos下,代表每个节拍的ms数 #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS定义了,说明要支持OS了(不限于UCOS). //当delay_us/delay_ms需要支持OS的时候需要三个与OS相关的宏定义和函数来支持 //首先是3个宏定义: // delay_osrunning:用于表示OS当前是否正在运行,以决定是否可以使用相关函数 //delay_ostickspersec:用于表示OS设定的时钟节拍,delay_init将根据这个参数来初始哈systick // delay_osintnesting:用于表示OS中断嵌套级别,因为中断里面不可以调度,delay_ms使用该参数来决定如何运行 //然后是3个函数: // delay_osschedlock:用于锁定OS任务调度,禁止调度 //delay_osschedunlock:用于解锁OS任务调度,重新开启调度 // delay_ostimedly:用于OS延时,可以引起任务调度. //本例程仅作UCOSII和UCOSIII的支持,其他OS,请自行参考着移植 //支持UCOSII #ifdef OS_CRITICAL_METHOD //OS_CRITICAL_METHOD定义了,说明要支持UCOSII #define delay_osrunning OSRunning //OS是否运行标记,0,不运行;1,在运行 #define delay_ostickspersec OS_TICKS_PER_SEC //OS时钟节拍,即每秒调度次数 #define delay_osintnesting OSIntNesting //中断嵌套级别,即中断嵌套次数 #endif //支持UCOSIII #ifdef CPU_CFG_CRITICAL_METHOD //CPU_CFG_CRITICAL_METHOD定义了,说明要支持UCOSIII #define delay_osrunning OSRunning //OS是否运行标记,0,不运行;1,在运行 #define delay_ostickspersec OSCfg_TickRate_Hz //OS时钟节拍,即每秒调度次数 #define delay_osintnesting OSIntNestingCtr //中断嵌套级别,即中断嵌套次数 #endif //us级延时时,关闭任务调度(防止打断us级延迟) void delay_osschedlock(void) { #ifdef CPU_CFG_CRITICAL_METHOD //使用UCOSIII OS_ERR err; OSSchedLock(&err); //UCOSIII的方式,禁止调度,防止打断us延时 #else //否则UCOSII OSSchedLock(); //UCOSII的方式,禁止调度,防止打断us延时 #endif } //us级延时时,恢复任务调度 void delay_osschedunlock(void) { #ifdef CPU_CFG_CRITICAL_METHOD //使用UCOSIII OS_ERR err; OSSchedUnlock(&err); //UCOSIII的方式,恢复调度 #else //否则UCOSII OSSchedUnlock(); //UCOSII的方式,恢复调度 #endif } //调用OS自带的延时函数延时 //ticks:延时的节拍数 void delay_ostimedly(u32 ticks) { #ifdef CPU_CFG_CRITICAL_METHOD OS_ERR err; OSTimeDly(ticks,OS_OPT_TIME_PERIODIC,&err); //UCOSIII延时采用周期模式 #else OSTimeDly(ticks); //UCOSII延时 #endif } //systick中断服务函数,使用ucos时用到 void SysTick_Handler(void) { if(delay_osrunning==1) //OS开始跑了,才执行正常的调度处理 { OSIntEnter(); //进入中断 OSTimeTick(); //调用ucos的时钟服务程序 OSIntExit(); //触发任务切换软中断 } } #endif //初始化延迟函数 //当使用OS的时候,此函数会初始化OS的时钟节拍 //SYSTICK的时钟固定为HCLK时钟的1/8 //SYSCLK:系统时钟 void delay_init() { #if SYSTEM_SUPPORT_OS //如果需要支持OS. u32 reload; #endif SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8 fac_us=SystemCoreClock/8000000; //为系统时钟的1/8 #if SYSTEM_SUPPORT_OS //如果需要支持OS. reload=SystemCoreClock/8000000; //每秒钟的计数次数 单位为M reload*=1000000/delay_ostickspersec; //根据delay_ostickspersec设定溢出时间 //reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右 fac_ms=1000/delay_ostickspersec; //代表OS可以延时的最少单位 SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断 SysTick->LOAD=reload; //每1/delay_ostickspersec秒中断一次 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK #else fac_ms=(u16)fac_us*1000; //非OS下,代表每个ms需要的systick时钟数 #endif } #if SYSTEM_SUPPORT_OS //如果需要支持OS. //延时nus //nus为要延时的us数. void delay_us(u32 nus) { u32 ticks; u32 told,tnow,tcnt=0; u32 reload=SysTick->LOAD; //LOAD的值 ticks=nusfac_us; //需要的节拍数 tcnt=0; delay_osschedlock(); //阻止OS调度,防止打断us延时 told=SysTick->VAL; //刚进入时的计数器值 while(1) { tnow=SysTick->VAL; if(tnow!=told) { if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了. else tcnt+=reload-tnow+told; told=tnow; if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出. } }; delay_osschedunlock(); //恢复OS调度 } //延时nms //nms:要延时的ms数 void delay_ms(u16 nms) { if(delay_osrunning&&delay_osintnesting==0) //如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度) { if(nms>=fac_ms) //延时的时间大于OS的最少时间周期 { delay_ostimedly(nms/fac_ms); //OS延时 } nms%=fac_ms; //OS已经无法提供这么小的延时了,采用普通方式延时 } delay_us((u32)(nms1000)); //普通方式延时 } #else //不用OS时 //延时nus //nus为要延时的us数. void delay_us(u32 nus) { u32 temp; SysTick->LOAD=nusfac_us; //时间加载 SysTick->VAL=0x00; //清空计数器 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器 SysTick->VAL =0X00; //清空计数器 } //延时nms //注意nms的范围 //SysTick->LOAD为24位寄存器,所以,最大延时为: //nms<=0xffffff81000/SYSCLK //SYSCLK单位为Hz,nms单位为ms //对72M条件下,nms<=1864 void delay_ms(u16 nms) { u32 temp; SysTick->LOAD=(u32)nmsfac_ms; //时间加载(SysTick->LOAD为24bit) SysTick->VAL =0x00; //清空计数器 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器 SysTick->VAL =0X00; //清空计数器 } #endif 现在的代码小车在避障后就会进入旋转的死循环

#include "balance.h" #include "TrackModule.h" int Time_count=0; //Time variable //计时变量 u8 Lidar_Detect = Lidar_Detect_ON; //电磁巡线模式雷达检测障碍物,默认开启 int RC_Lidar_Avoid_FLAG = RC_Avoid_OFF; //遥控模式雷达避障,默认关闭 u8 Mode; float RC_Velocity_CCD=350,RC_Velocity_ELE=350; float PS2_Velocity,PS2_Turn_Velocity; //遥控控制的速度 Encoder OriginalEncoder; //Encoder raw data //编码器原始数据 u8 Lidar_Success_Receive_flag=0,Lidar_flag_count=0;//雷达在线标志位,雷达掉线计数器 //粗延时函数,微秒 void delay1_us(u16 time) { u16 i=0; while(time--) { i=10; //自己定义 while(i--) ; } } //毫秒级的延时 void delay1_ms(u16 time) { u16 i=0; while(time--) { i=12000; //自己定义 while(i--) ; } } /************************************************************************** Function: The inverse kinematics solution is used to calculate the target speed of each wheel according to the target speed of three axes Input : X and Y, Z axis direction of the target movement speed Output : none 函数功能:运动学逆解,根据三轴目标速度计算各车轮目标转速 入口参数:X和Y、Z轴方向的目标运动速度 返回 值:无 **************************************************************************/ void Drive_Motor(float Vx,float Vy,float Vz) { float amplitude=3.5; //Wheel target speed limit //车轮目标速度限幅 // if((Mode == PS2_Control_Mode||Mode == APP_Control_Mode)&&RC_Lidar_Avoid_FLAG&&Lidar_Success_Receive_flag)//app以及ps2控制下,判断是否开启遥控避障 // { // RC_Lidar_Aviod(&Vx,&Vy,&Vz); // } // //Speed smoothing is enabled when moving the omnidirectional trolley // //全向移动小车才开启速度平滑处理 // if(Car_Mode==Mec_Car||Car_Mode==Omni_Car) // { // Smooth_control(Vx,Vy,Vz); //Smoothing the input speed //对输入速度进行平滑处理 // // //Get the smoothed data // //获取平滑处理后的数据 // Vx=smooth_control.VX; // Vy=smooth_control.VY; // Vz=smooth_control.VZ; // } // // //Mecanum wheel car // //麦克纳姆轮小车 // if (Car_Mode==Mec_Car) // { // //Inverse kinematics //运动学逆解 // MOTOR_A.Target = +Vy+Vx-Vz*(Axle_spacing+Wheel_spacing); // MOTOR_B.Target = -Vy+Vx-Vz*(Axle_spacing+Wheel_spacing); // MOTOR_C.Target = +Vy+Vx+Vz*(Axle_spacing+Wheel_spacing); // MOTOR_D.Target = -Vy+Vx+Vz*(Axle_spacing+Wheel_spacing); // // //Wheel (motor) target speed limit //车轮(电机)目标速度限幅 // MOTOR_A.Target=target_limit_float(MOTOR_A.Target,-amplitude,amplitude); // MOTOR_B.Target=target_limit_float(MOTOR_B.Target,-amplitude,amplitude); // MOTOR_C.Target=target_limit_float(MOTOR_C.Target,-amplitude,amplitude); // MOTOR_D.Target=target_limit_float(MOTOR_D.Target,-amplitude,amplitude); // } // // //Omni car // //全向轮小车 // else if (Car_Mode==Omni_Car) // { // //Inverse kinematics //运动学逆解 // MOTOR_A.Target = Vy + Omni_turn_radiaus*Vz; // MOTOR_B.Target = -X_PARAMETER*Vx - Y_PARAMETER*Vy + Omni_turn_radiaus*Vz; // MOTOR_C.Target = +X_PARAMETER*Vx - Y_PARAMETER*Vy + Omni_turn_radiaus*Vz; // // //Wheel (motor) target speed limit //车轮(电机)目标速度限幅 // MOTOR_A.Target=target_limit_float(MOTOR_A.Target,-amplitude,amplitude); // MOTOR_B.Target=target_limit_float(MOTOR_B.Target,-amplitude,amplitude); // MOTOR_C.Target=target_limit_float(MOTOR_C.Target,-amplitude,amplitude); // MOTOR_D.Target=0; //Out of use //没有使用到 // } // // //Ackermann structure car // //阿克曼小车 // else if (Car_Mode==Akm_Car) // { // //Ackerman car specific related variables //阿克曼小车专用相关变量 // float R, Ratio=636.56, AngleR, Angle_Servo; // // // For Ackerman small car, Vz represents the front wheel steering Angle // //对于阿克曼小车Vz代表右前轮转向角度 // AngleR=Vz; // R=Axle_spacing/tan(AngleR)-0.5f*Wheel_spacing; // // // Front wheel steering Angle limit (front wheel steering Angle controlled by steering engine), unit: rad // //前轮转向角度限幅(舵机控制前轮转向角度),单位:rad // AngleR=target_limit_float(AngleR,-0.49f,0.32f); // // //Inverse kinematics //运动学逆解 // if(AngleR!=0) // { // MOTOR_A.Target = Vx*(R-0.5f*Wheel_spacing)/R; // MOTOR_B.Target = Vx*(R+0.5f*Wheel_spacing)/R; // } // else // { // MOTOR_A.Target = Vx; // MOTOR_B.Target = Vx; // } // // The PWM value of the servo controls the steering Angle of the front wheel // //舵机PWM值,舵机控制前轮转向角度 // //Angle_Servo = -0.628f*pow(AngleR, 3) + 1.269f*pow(AngleR, 2) - 1.772f*AngleR + 1.573f; // Angle_Servo = -0.628f*pow(AngleR, 3) + 1.269f*pow(AngleR, 2) - 1.772f*AngleR + 1.755f; // Servo=SERVO_INIT + (Angle_Servo - 1.755f)*Ratio; // // //Wheel (motor) target speed limit //车轮(电机)目标速度限幅 // MOTOR_A.Target=target_limit_float(MOTOR_A.Target,-amplitude,amplitude); // MOTOR_B.Target=target_limit_float(MOTOR_B.Target,-amplitude,amplitude); // MOTOR_C.Target=0; //Out of use //没有使用到 // MOTOR_D.Target=0; //Out of use //没有使用到 // Servo=target_limit_int(Servo,800,2200); //Servo PWM value limit //舵机PWM值限幅 // } // // //Differential car // //差速小车 // else if (Car_Mode==Diff_Car) // { //Inverse kinematics //运动学逆解 MOTOR_A.Target = Vx - Vz * Wheel_spacing / 2.0f; //计算出左轮的目标速度 MOTOR_B.Target = Vx + Vz * Wheel_spacing / 2.0f; //计算出右轮的目标速度 //Wheel (motor) target speed limit //车轮(电机)目标速度限幅 MOTOR_A.Target=target_limit_float( MOTOR_A.Target,-amplitude,amplitude); MOTOR_B.Target=target_limit_float( MOTOR_B.Target,-amplitude,amplitude); MOTOR_C.Target=0; //Out of use //没有使用到 MOTOR_D.Target=0; //Out of use //没有使用到 // } // // //FourWheel car // //四驱车 // else if(Car_Mode==FourWheel_Car) // { // //Inverse kinematics //运动学逆解 // MOTOR_A.Target = Vx - Vz * (Wheel_spacing + Axle_spacing) / 2.0f; //计算出左轮的目标速度 // MOTOR_B.Target = Vx - Vz * (Wheel_spacing + Axle_spacing) / 2.0f; //计算出左轮的目标速度 // MOTOR_C.Target = Vx + Vz * (Wheel_spacing + Axle_spacing) / 2.0f; //计算出右轮的目标速度 // MOTOR_D.Target = Vx + Vz * (Wheel_spacing + Axle_spacing) / 2.0f; //计算出右轮的目标速度 // // //Wheel (motor) target speed limit //车轮(电机)目标速度限幅 // MOTOR_A.Target=target_limit_float( MOTOR_A.Target,-amplitude,amplitude); // MOTOR_B.Target=target_limit_float( MOTOR_B.Target,-amplitude,amplitude); // MOTOR_C.Target=target_limit_float( MOTOR_C.Target,-amplitude,amplitude); // MOTOR_D.Target=target_limit_float( MOTOR_D.Target,-amplitude,amplitude); // } // // //Tank Car // //履带车 // else if (Car_Mode==Tank_Car) // { // //Inverse kinematics //运动学逆解 // MOTOR_A.Target = Vx - Vz * (Wheel_spacing) / 2.0f; //计算出左轮的目标速度 // MOTOR_B.Target = Vx + Vz * (Wheel_spacing) / 2.0f; //计算出右轮的目标速度 // // //Wheel (motor) target speed limit //车轮(电机)目标速度限幅 // MOTOR_A.Target=target_limit_float( MOTOR_A.Target,-amplitude,amplitude); // MOTOR_B.Target=target_limit_float( MOTOR_B.Target,-amplitude,amplitude); // MOTOR_C.Target=0; //Out of use //没有使用到 // MOTOR_D.Target=0; //Out of use //没有使用到 // } } /************************************************************************** Function: FreerTOS task, core motion control task Input : none Output : none 函数功能:FreeRTOS任务,核心运动控制任务 入口参数:无 返回 值:无 **************************************************************************/ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { static u8 Count_CCD = 0; //调节CCD控制频率 static u8 last_mode = 0; if(htim->Instance==TIM12) { // //TIM12->SR &= ~(0x01); // //Time count is no longer needed after 30 seconds // //时间计数,30秒后不再需要 // if(Time_count<3000)Time_count++; // //Get the encoder data, that is, the real time wheel speed, // //and convert to transposition international units // //获取编码器数据,即车轮实时速度,并转换位国际单位 // Get_Velocity_Form_Encoder(); //// if(++Lidar_flag_count==10) Lidar_Success_Receive_flag=0,Lidar_flag_count=0,RC_Lidar_Avoid_FLAG=RC_Avoid_OFF; //100ms清零雷达接收到数据标志位 //// switch(User_Key_Scan()) //// { //// case 1: //单击用来切换模式 //// Mode++; //// if(Mode == ELE_Line_Patrol_Mode) //// { //// ELE_ADC_Init(); //初始化电磁巡线模式 //// } ////// //// else if(Mode == CCD_Line_Patrol_Mode) //CCD巡线模式 //// { //// CCD_Init(); //CCD初始化,CCD模块和电磁巡线模块共用一个接口,两个不能同时使用 //// } //// else if(Mode > 6) //5种模式循环切换 //// { //// Mode = 0; //// } //// break; //// case 2: //电磁巡线状态时,双击可以打开/关闭雷达检测障碍物,默认打开 //// if(Mode == ELE_Line_Patrol_Mode) //电磁巡线控制模式下 //// { //// Lidar_Detect = !Lidar_Detect; //// if(Lidar_Detect == Lidar_Detect_OFF) //// memset(Dataprocess,0, sizeof(PointDataProcessDef)*225); //用于雷达检测障碍物的数组清零 //// }else if(Mode == APP_Control_Mode||Mode ==PS2_Control_Mode){ //// RC_Lidar_Avoid_FLAG=!RC_Lidar_Avoid_FLAG; //// if(RC_Lidar_Avoid_FLAG == RC_Avoid_OFF) //// memset(Dataprocess,0, sizeof(PointDataProcessDef)*225); //用于雷达检测障碍物的数组清零 //// } //// break; //// } //// if(last_mode != Mode) //每次切换模式需要刷新屏幕 //// { //// last_mode++; //// OLED_Clear(); //// if(last_mode>6) //// last_mode = 0; //// } //// //// if(Mode != ELE_Line_Patrol_Mode) //// Buzzer_Alarm(0); //// if(Mode == APP_Control_Mode) Get_RC(); //Handle the APP remote commands //处理APP遥控命令 //// else if(Mode == PS2_Control_Mode) PS2_Control(); //Handle PS2 controller commands //处理PS2手柄控制命令 //// else if(Mode == Lidar_Avoid_Mode) Lidar_Avoid(); //Avoid Mode //避障模式 //// else if(Mode == Lidar_Follow_Mode) Lidar_Follow(); //Follow Mode //跟随模式 //// else if(Mode == Lidar_Along_Mode) Lidar_along_wall(); //Along Mode //走直线模式 //// else if(Mode == ELE_Line_Patrol_Mode) //// { //// //// //// Get_RC_ELE(); //ELE模式 ////// delay1_ms(2); //// } //// else //CCD模式 //// { //// if(++Count_CCD == 2) //调节控制频率,4*5 = 20ms控制一次 //// { //// Count_CCD = 0; //// //// Get_RC_CCD(); //// } //// else if(Count_CCD>2) //// Count_CCD = 0; //// } // // // IRDM_line_inspection_V2(); // Drive_Motor(Move_X,Move_Y,Move_Z); // // // //If there is no abnormity in the battery voltage, and the enable switch is in the ON position, // //and the software failure flag is 0 // //如果电池电压不存在异常,而且使能开关在ON档位,而且软件失能标志位为0 // if(Turn_Off(Voltage)==0) // { // //Speed closed-loop control to calculate the PWM value of each motor, // //PWM represents the actual wheel speed // //速度闭环控制计算各电机PWM值,PWM代表车轮实际转速 // MOTOR_A.Motor_Pwm=Incremental_PI_A(MOTOR_A.Encoder, MOTOR_A.Target); // MOTOR_B.Motor_Pwm=Incremental_PI_B(MOTOR_B.Encoder, MOTOR_B.Target); // MOTOR_C.Motor_Pwm=Incremental_PI_C(MOTOR_C.Encoder, MOTOR_C.Target); // MOTOR_D.Motor_Pwm=Incremental_PI_D(MOTOR_D.Encoder, MOTOR_D.Target); // Limit_Pwm(16500) ; // //Set different PWM control polarity according to different car models // //根据不同小车型号设置不同的PWM控制极性 // switch(Car_Mode) // { // case Mec_Car: Set_Pwm(-MOTOR_A.Motor_Pwm, -MOTOR_B.Motor_Pwm, MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //Mecanum wheel car //麦克纳姆轮小车 // case Omni_Car: Set_Pwm( MOTOR_A.Motor_Pwm, MOTOR_B.Motor_Pwm, MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //Omni car //全向轮小车 // case Akm_Car: Set_Pwm(-MOTOR_A.Motor_Pwm, MOTOR_B.Motor_Pwm, MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, Servo); break; //Ackermann structure car //阿克曼小车 // case Diff_Car: Set_Pwm(-MOTOR_A.Motor_Pwm, MOTOR_B.Motor_Pwm, MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //Differential car //两轮差速小车 // case FourWheel_Car: Set_Pwm(-MOTOR_A.Motor_Pwm, -MOTOR_B.Motor_Pwm, MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //FourWheel car //四驱车 // case Tank_Car: Set_Pwm(-MOTOR_A.Motor_Pwm, MOTOR_B.Motor_Pwm, MOTOR_C.Motor_Pwm, MOTOR_D.Motor_Pwm, 0 ); break; //Tank Car //履带车 // } // } // //If Turn_Off(Voltage) returns to 1, the car is not allowed to move, and the PWM value is set to 0 // //如果Turn_Off(Voltage)返回值为1,不允许控制小车进行运动,PWM值设置为0 // else Set_Pwm(0,0,0,0,0); //----------------云台移植测试-------------------// Xianfu_Pwm(); Velocity1=Position_PID_1(Position1,Target1); //舵机PID控制,可以根据目标位置进行速度调整,离目标位置越远速度越快 Velocity2=Position_PID_2(Position2,Target2); //舵机PID控制,可以根据目标位置进行速度调整,离目标位置越远速度越快 Xianfu_Velocity(); Set_Pwm_PTZ(Velocity1,Velocity2); //赋值给PWM寄存器 } } /************************************************************************** Function: Assign a value to the PWM register to control wheel speed and direction Input : PWM Output : none 函数功能:赋值给PWM寄存器,控制车轮转速与方向 入口参数:PWM 返回 值:无 **************************************************************************/ void Set_Pwm(int motor_a,int motor_b,int motor_c,int motor_d,int servo) { if(motor_a<0) PWMA2=16800,PWMA1=16800+motor_a; else PWMA1=16800,PWMA2=16800-motor_a; if(motor_b<0) PWMB1=16800,PWMB2=16800+motor_b; else PWMB2=16800,PWMB1=16800-motor_b; if(motor_c<0) PWMC1=16800,PWMC2=16800+motor_c; else PWMC2=16800,PWMC1=16800-motor_c; if(motor_d<0) PWMD2=16800,PWMD1=16800+motor_d; else PWMD1=16800,PWMD2=16800-motor_d; //Servo control //舵机控制 Servo_PWM = servo; } /************************************************************************** Function: Limit PWM value Input : Value Output : none 函数功能:限制PWM值 入口参数:幅值 返回 值:无 **************************************************************************/ void Limit_Pwm(int amplitude) { MOTOR_A.Motor_Pwm=target_limit_float(MOTOR_A.Motor_Pwm,-amplitude,amplitude); MOTOR_B.Motor_Pwm=target_limit_float(MOTOR_B.Motor_Pwm,-amplitude,amplitude); MOTOR_C.Motor_Pwm=target_limit_float(MOTOR_C.Motor_Pwm,-amplitude,amplitude); MOTOR_D.Motor_Pwm=target_limit_float(MOTOR_D.Motor_Pwm,-amplitude,amplitude); } /************************************************************************** Function: Limiting function Input : Value Output : none 函数功能:限幅函数 入口参数:幅值 返回 值:无 **************************************************************************/ float target_limit_float(float insert,float low,float high) { if (insert < low) return low; else if (insert > high) return high; else return insert; } int target_limit_int(int insert,int low,int high) { if (insert < low) return low; else if (insert > high) return high; else return insert; } /************************************************************************** Function: Check the battery voltage, enable switch status, software failure flag status Input : Voltage Output : Whether control is allowed, 1: not allowed, 0 allowed 函数功能:检查电池电压、使能开关状态、软件失能标志位状态 入口参数:电压 返回 值:是否允许控制,1:不允许,0允许 **************************************************************************/ u8 Turn_Off( int voltage) { u8 temp; if(voltage<10||EN==0||Flag_Stop==1) { temp=1; } else temp=0; return temp; } /************************************************************************** Function: Calculate absolute value Input : long int Output : unsigned int 函数功能:求绝对值 入口参数:long int 返回 值:unsigned int **************************************************************************/ u32 myabs(long int a) { u32 temp; if(a<0) temp=-a; else temp=a; return temp; } /************************************************************************** Function: Incremental PI controller Input : Encoder measured value (actual speed), target speed Output : Motor PWM According to the incremental discrete PID formula pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)] e(k) represents the current deviation e(k-1) is the last deviation and so on PWM stands for incremental output In our speed control closed loop system, only PI control is used pwm+=Kp[e(k)-e(k-1)]+Ki*e(k) 函数功能:增量式PI控制器 入口参数:编码器测量值(实际速度),目标速度 返回 值:电机PWM 根据增量式离散PID公式 pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)] e(k)代表本次偏差 e(k-1)代表上一次的偏差 以此类推 pwm代表增量输出 在我们的速度控制闭环系统里面,只使用PI控制 pwm+=Kp[e(k)-e(k-1)]+Ki*e(k) **************************************************************************/ int Incremental_PI_A (float Encoder,float Target) { static float Bias,Pwm,Last_bias; Bias=Target-Encoder; //Calculate the deviation //计算偏差 Pwm+=Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias; if(Pwm>16800)Pwm=16800; if(Pwm<-16800)Pwm=-16800; Last_bias=Bias; //Save the last deviation //保存上一次偏差 return Pwm; } int Incremental_PI_B (float Encoder,float Target) { static float Bias,Pwm,Last_bias; Bias=Target-Encoder; //Calculate the deviation //计算偏差 Pwm+=Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias; if(Pwm>16800)Pwm=16800; if(Pwm<-16800)Pwm=-16800; Last_bias=Bias; //Save the last deviation //保存上一次偏差 return Pwm; } int Incremental_PI_C (float Encoder,float Target) { static float Bias,Pwm,Last_bias; Bias=Target-Encoder; //Calculate the deviation //计算偏差 Pwm+=Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias; if(Pwm>16800)Pwm=16800; if(Pwm<-16800)Pwm=-16800; Last_bias=Bias; //Save the last deviation //保存上一次偏差 return Pwm; } int Incremental_PI_D (float Encoder,float Target) { static float Bias,Pwm,Last_bias; Bias=Target-Encoder; //Calculate the deviation //计算偏差 Pwm+=Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias; if(Pwm>16800)Pwm=16800; if(Pwm<-16800)Pwm=-16800; Last_bias=Bias; //Save the last deviation //保存上一次偏差 return Pwm; } /************************************************************************** Function: Processes the command sent by APP through usart 2 Input : none Output : none 函数功能:对APP通过串口2发送过来的命令进行处理 入口参数:无 返回 值:无 **************************************************************************/ void Get_RC(void) { u8 Flag_Move=1; if(Car_Mode==Mec_Car||Car_Mode==Omni_Car) //The omnidirectional wheel moving trolley can move laterally //全向轮运动小车可以进行横向移动 { switch(Flag_Direction) //Handle direction control commands //处理方向控制命令 { case 1: Move_X=RC_Velocity; Move_Y=0; Flag_Move=1; break; case 2: Move_X=RC_Velocity; Move_Y=-RC_Velocity; Flag_Move=1; break; case 3: Move_X=0; Move_Y=-RC_Velocity; Flag_Move=1; break; case 4: Move_X=-RC_Velocity; Move_Y=-RC_Velocity; Flag_Move=1; break; case 5: Move_X=-RC_Velocity; Move_Y=0; Flag_Move=1; break; case 6: Move_X=-RC_Velocity; Move_Y=RC_Velocity; Flag_Move=1; break; case 7: Move_X=0; Move_Y=RC_Velocity; Flag_Move=1; break; case 8: Move_X=RC_Velocity; Move_Y=RC_Velocity; Flag_Move=1; break; default: Move_X=0; Move_Y=0; Flag_Move=0; break; } if(Flag_Move==0) { //If no direction control instruction is available, check the steering control status //如果无方向控制指令,检查转向控制状态 if (Flag_Left ==1) Move_Z= PI/2*(RC_Velocity/500); //left rotation //左自转 else if(Flag_Right==1) Move_Z=-PI/2*(RC_Velocity/500); //right rotation //右自转 else Move_Z=0; //stop //停止 } } else //Non-omnidirectional moving trolley //非全向移动小车 { switch(Flag_Direction) //Handle direction control commands //处理方向控制命令 { case 1: Move_X=+RC_Velocity; Move_Z=0; break; case 2: Move_X=+RC_Velocity; Move_Z=-PI/2; break; case 3: Move_X=0; Move_Z=-PI/2; break; case 4: Move_X=-RC_Velocity; Move_Z=-PI/2; break; case 5: Move_X=-RC_Velocity; Move_Z=0; break; case 6: Move_X=-RC_Velocity; Move_Z=+PI/2; break; case 7: Move_X=0; Move_Z=+PI/2; break; case 8: Move_X=+RC_Velocity; Move_Z=+PI/2; break; default: Move_X=0; Move_Z=0; break; } if (Flag_Left ==1) Move_Z= PI/2; //left rotation //左自转 else if(Flag_Right==1) Move_Z=-PI/2; //right rotation //右自转 } //Z-axis data conversion //Z轴数据转化 if(Car_Mode==Akm_Car) { //Ackermann structure car is converted to the front wheel steering Angle system target value, and kinematics analysis is pearformed //阿克曼结构小车转换为前轮转向角度 Move_Z=Move_Z*2/9; } else if(Car_Mode==Diff_Car||Car_Mode==Tank_Car||Car_Mode==FourWheel_Car) { if(Move_X<0) Move_Z=-Move_Z; //The differential control principle series requires this treatment //差速控制原理系列需要此处理 Move_Z=Move_Z*RC_Velocity/500; } //Unit conversion, mm/s -> m/s //单位转换,mm/s -> m/s Move_X=Move_X/1000; Move_Y=Move_Y/1000; Move_Z=Move_Z; //Control target value is obtained and kinematics analysis is performed //得到控制目标值,进行运动学分析 Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** Function: Read the encoder value and calculate the wheel speed, unit m/s Input : none Output : none 函数功能:读取编码器数值并计算车轮速度,单位m/s 入口参数:无 返回 值:无 **************************************************************************/ void Get_Velocity_Form_Encoder(void) { //Retrieves the original data of the encoder //获取编码器的原始数据 float Encoder_A_pr,Encoder_B_pr,Encoder_C_pr,Encoder_D_pr; OriginalEncoder.A=Read_Encoder(2); OriginalEncoder.B=Read_Encoder(3); OriginalEncoder.C=Read_Encoder(4); OriginalEncoder.D=Read_Encoder(5); //Decide the encoder numerical polarity according to different car models //根据不同小车型号决定编码器数值极性 switch(Car_Mode) { case Mec_Car: Encoder_A_pr=OriginalEncoder.A; Encoder_B_pr=OriginalEncoder.B; Encoder_C_pr= -OriginalEncoder.C; Encoder_D_pr= -OriginalEncoder.D; break; case Omni_Car: Encoder_A_pr=-OriginalEncoder.A; Encoder_B_pr=-OriginalEncoder.B; Encoder_C_pr= -OriginalEncoder.C; Encoder_D_pr= OriginalEncoder.D; break; case Akm_Car: Encoder_A_pr=OriginalEncoder.A; Encoder_B_pr=-OriginalEncoder.B; Encoder_C_pr= OriginalEncoder.C; Encoder_D_pr= OriginalEncoder.D; break; case Diff_Car: Encoder_A_pr=OriginalEncoder.A; Encoder_B_pr=-OriginalEncoder.B; Encoder_C_pr= OriginalEncoder.C; Encoder_D_pr= OriginalEncoder.D; break; case FourWheel_Car: Encoder_A_pr=OriginalEncoder.A; Encoder_B_pr=OriginalEncoder.B; Encoder_C_pr= -OriginalEncoder.C; Encoder_D_pr= -OriginalEncoder.D; break; case Tank_Car: Encoder_A_pr=OriginalEncoder.A; Encoder_B_pr= -OriginalEncoder.B; Encoder_C_pr= OriginalEncoder.C; Encoder_D_pr= OriginalEncoder.D; break; } //The encoder converts the raw data to wheel speed in m/s //编码器原始数据转换为车轮速度,单位m/s MOTOR_A.Encoder= Encoder_A_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; MOTOR_B.Encoder= Encoder_B_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; MOTOR_C.Encoder= Encoder_C_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; MOTOR_D.Encoder= Encoder_D_pr*CONTROL_FREQUENCY*Wheel_perimeter/Encoder_precision; } /************************************************************************** Function: Smoothing the three axis target velocity Input : Three-axis target velocity Output : none 函数功能:对三轴目标速度做平滑处理 入口参数:三轴目标速度 返回 值:无 **************************************************************************/ void Smooth_control(float vx,float vy,float vz) { float step=0.05; if (vx>0) smooth_control.VX+=step; else if(vx<0) smooth_control.VX-=step; else if(vx==0) smooth_control.VX=smooth_control.VX*0.9f; if (vy>0) smooth_control.VY+=step; else if(vy<0) smooth_control.VY-=step; else if(vy==0) smooth_control.VY=smooth_control.VY*0.9f; if (vz>0) smooth_control.VZ+=step; else if(vz<0) smooth_control.VZ-=step; else if(vz==0) smooth_control.VZ=smooth_control.VZ*0.9f; smooth_control.VX=target_limit_float(smooth_control.VX,-float_abs(vx),float_abs(vx)); smooth_control.VY=target_limit_float(smooth_control.VY,-float_abs(vy),float_abs(vy)); smooth_control.VZ=target_limit_float(smooth_control.VZ,-float_abs(vz),float_abs(vz)); } /************************************************************************** Function: Floating-point data calculates the absolute value Input : float Output : The absolute value of the input number 函数功能:浮点型数据计算绝对值 入口参数:浮点数 返回 值:输入数的绝对值 **************************************************************************/ float float_abs(float insert) { if(insert>=0) return insert; else return -insert; } /************************************************************************** Function: PS2_Control Input : none Output : none 函数功能:PS2手柄控制 入口参数: 无 返回 值:无 **************************************************************************/ void PS2_Control(void) { int LY,RX,LX; //手柄ADC的值 int Threshold=20; //阈值,忽略摇杆小幅度动作 static float Key1_Count = 0,Key2_Count = 0; //用于控制读取摇杆的速度 //转化为128到-128的数值 LY=-(PS2_LY-128);//左边Y轴控制前进后退 RX=-(PS2_RX-128);//右边X轴控制转向 LX=-(PS2_LX-128);//左边X轴控制转向,麦轮和全向小车专用 if(LY>-Threshold&&LY<Threshold) LY=0; if(RX>-Threshold&&RX<Threshold) RX=0; //忽略摇杆小幅度动作 if(LX>-Threshold&&LX<Threshold) LX=0; if(Strat) //按下start键才可以控制小车 { if (PS2_KEY == PSB_L1) //按下左1键加速(按键在顶上) { if((++Key1_Count) == 20) //调节按键反应速度 { PS2_KEY = 0; Key1_Count = 0; if((PS2_Velocity += X_Step)>MAX_RC_Velocity) //前进最大速度1230 PS2_Velocity = MAX_RC_Velocity; if(Car_Mode != Akm_Car) //非阿克曼车可调节转向速度 { if((PS2_Turn_Velocity += Z_Step)>MAX_RC_Turn_Bias) //转向最大速度325 PS2_Turn_Velocity = MAX_RC_Turn_Bias; } } } else if(PS2_KEY == PSB_R1) //按下右1键减速 { if((++Key2_Count) == 15) { PS2_KEY = 0; Key2_Count = 0; if((PS2_Velocity -= X_Step)<MINI_RC_Velocity) //前后最小速度110 PS2_Velocity = MINI_RC_Velocity; if(Car_Mode != Akm_Car) //非阿克曼车可调节转向速度 { if((PS2_Turn_Velocity -= Z_Step)<MINI_RC_Turn_Velocity)//转向最小速度45 PS2_Turn_Velocity = MINI_RC_Turn_Velocity; } } } else Key2_Count = 0,Key2_Count = 0; //读取到其他按键重新计数 Move_X = (PS2_Velocity/128)*LY; //速度控制,力度表示速度大小 if(Car_Mode == Mec_Car || Car_Mode == Omni_Car) { Move_Y = LX*PS2_Velocity/128; } else { Move_Y = 0; } if(Car_Mode == Akm_Car) //阿克曼车转向控制,力度表示转向角度 Move_Z = RX*(PI/2)/128*2/9; else //其他车型转向控制 { //if(Move_X>=0) Move_Z = (PS2_Turn_Velocity/128)*RX; //转向控制,力度表示转向速度 // else // Move_Z = -(PS2_Turn_Velocity/128)*RX; } } else { Move_X = 0; Move_Y = 0; Move_Z = 0; } Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** 函数功能:CCD巡线,采集3个电感的数据并提取中线 入口参数:无 返回 值:无 **************************************************************************/ void Get_RC_CCD(void) { static float Bias,Last_Bias; float move_z=0; Move_X=RC_Velocity_CCD; //CCD巡线模式线速度 Bias=CCD_Median-64; //提取偏差,64为巡线的中心点 if(Car_Mode == Omni_Car) move_z=-Bias*Omni_Car_CCD_KP*0.1f-(Bias-Last_Bias)*Omni_Car_CCD_KI*0.1f; //PD控制,原理就是使得小车保持靠近巡线的中心点 else if(Car_Mode == Tank_Car) move_z=-Bias*Tank_Car_CCD_KP*0.1f-(Bias-Last_Bias)*Tank_Car_CCD_KI*0.1f; else move_z=-Bias*CCD_KP*0.1f-(Bias-Last_Bias)*CCD_KI*0.1f; Last_Bias=Bias; //保存上一次的偏差 if(Car_Mode==Mec_Car) { Move_Z=move_z*RC_Velocity_CCD/50000; //差速控制原理需要经过此处处理 } else if(Car_Mode==Omni_Car) { Move_Z=move_z*RC_Velocity_CCD/21000; //差速控制原理需要经过此处处理 } else if(Car_Mode==Akm_Car) { Move_Z=move_z/450; //差速控制原理需要经过此处处理 } else if(Car_Mode==Diff_Car) { if(Move_X<0) move_z=-move_z; Move_Z=move_z*RC_Velocity_CCD/67000; //差速控制原理需要经过此处处理 } else if(Car_Mode==Tank_Car) { if(Move_X<0) move_z=-move_z; Move_Z=move_z*RC_Velocity_CCD/50000; //差速控制原理需要经过此处处理 } else if(Car_Mode==FourWheel_Car) { if(Move_X<0) move_z=-move_z; Move_Z=move_z*RC_Velocity_CCD/20100; //差速控制原理需要经过此处处理 } //Z-axis data conversion //Z轴数据转化 //Unit conversion, mm/s -> m/s //单位转换,mm/s -> m/s Move_X=Move_X/1000; Move_Z=Move_Z; Move_Y=0; //Control target value is obtained and kinematics analysis is performed //得到控制目标值,进行运动学分析 Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** 函数功能:电磁巡线,采集3个电感的数据并提取中线 入口参数:无 返回 值:无 **************************************************************************/ void Get_RC_ELE(void) { static float Bias,Last_Bias; float move_z=0; if(Detect_Barrier() == No_Barrier) { Move_X=RC_Velocity_ELE; //电磁巡线模式的速度 Bias=100-Sensor; //提取偏差 if(Car_Mode == Omni_Car) move_z=-Bias* Omni_Car_ELE_KP*0.08f-(Bias-Last_Bias)* Omni_Car_ELE_KI*0.05f; else if(Car_Mode == Tank_Car) move_z=-Bias*Tank_Car_ELE_KP*0.1f-(Bias-Last_Bias)*Tank_Car_ELE_KI*0.1f; else move_z=-Bias* ELE_KP*0.08f-(Bias-Last_Bias)* ELE_KI*0.05f; Last_Bias=Bias; Buzzer_Alarm(0); if(Car_Mode==Mec_Car) { Move_Z=move_z*RC_Velocity_ELE/50000; //差速控制原理需要经过此处处理 } else if(Car_Mode==Omni_Car) { Move_Z=move_z*RC_Velocity_ELE/10800; //差速控制原理需要经过此处处理 } else if(Car_Mode==Diff_Car) { if(Move_X<0) move_z=-move_z; Move_Z=move_z*RC_Velocity_ELE/45000; //差速控制原理需要经过此处处理 } else if(Car_Mode==Tank_Car) { if(Move_X<0) move_z=-move_z; Move_Z=move_z*RC_Velocity_ELE/28000; //差速控制原理需要经过此处处理 } else if(Car_Mode==FourWheel_Car) { if(Move_X<0) move_z=-move_z; Move_Z=move_z*RC_Velocity_ELE/20100; //差速控制原理需要经过此处处理 } else if(Car_Mode==Akm_Car) { Move_Z=move_z/450; //差速控制原理需要经过此处处理 } } else //有障碍物 { Buzzer_Alarm(100); //当电机使能的时候,有障碍物则蜂鸣器报警 Move_X = 0; Move_Z = 0; Move_Y = 0; } //Z-axis data conversion //Z轴数据转化 //Unit conversion, mm/s -> m/s //单位转换,mm/s -> m/s Move_X=Move_X/1000; Move_Z=Move_Z; Move_Y=0; //Control target value is obtained and kinematics analysis is performed //得到控制目标值,进行运动学分析 Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** 函数功能:检测前方是否有障碍物 入口参数:无 返回 值:无 **************************************************************************/ u8 Detect_Barrier(void) { u16 i; u16 point_count = 0; if(Lidar_Detect == Lidar_Detect_ON) { for(i=0;i<1152;i++) //检测是否有障碍物 { if((Dataprocess[i].angle>300)||(Dataprocess[i].angle<60)) { if(0<Dataprocess[i].distance&&Dataprocess[i].distance<700)//700mm内是否有障碍物 point_count++; } } if(point_count > 0)//有障碍物 return Barrier_Detected; else return No_Barrier; } else return No_Barrier; } /************************************************************************** 函数功能:小车避障模式 入口参数:无 返回 值:无 **************************************************************************/ void Lidar_Avoid(void) { u16 i = 0; u8 calculation_angle_cnt = 0; //用于判断225个点中需要做避障的点 float angle_sum = 0; //粗略计算障碍物位于左或者右 u8 distance_count = 0; //距离小于某值的计数 for(i=0;i<1152;i++) //遍历120度范围内的距离数据,共120个点左右的数据 { if((Dataprocess[i].angle>300)||(Dataprocess[i].angle<60)) //避障角度在300-60之间 { if((0<Dataprocess[i].distance)&&(Dataprocess[i].distance<Avoid_Distance)) //距离小于450mm需要避障,只需要120度范围内点 { calculation_angle_cnt++; //计算距离小于避障距离的点个数 if(Dataprocess[i].angle<60) angle_sum += Dataprocess[i].angle; else if(Dataprocess[i].angle>300) angle_sum += (Dataprocess[i].angle-360); //300度到60度转化为-60度到60度 if(Dataprocess[i].distance<Avoid_Min_Distance) //记录小于200mm的点的计数 distance_count++; } } } Move_X = forward_velocity; if(calculation_angle_cnt == 0)//不需要避障 { Move_Z = 0; } else //当距离小于200mm,小车往后退 { if(distance_count>8) { Move_X = -forward_velocity; Move_Z = 0; } else { Move_X = 0; if(angle_sum > 0)//障碍物偏右 { if(Car_Mode == Mec_Car) //麦轮转弯需要把前进速度降低 Move_X = 0; else //其他车型保持原有车速 Move_X = forward_velocity; if(Car_Mode == Akm_Car) Move_Z = PI/4; else if(Car_Mode == Omni_Car) Move_Z=corner_velocity; else Move_Z=other_corner_velocity;//左转 } else //偏左 { if(Car_Mode == Mec_Car) Move_X = 0; else Move_X = forward_velocity; if(Car_Mode == Akm_Car) Move_Z = -PI/4; else if(Car_Mode == Omni_Car) Move_Z=-corner_velocity;//右转 else Move_Z=-other_corner_velocity; } } } Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** 函数功能:小车跟随模式 入口参数:无 返回 值:无 **************************************************************************/ void Lidar_Follow(void) { static u16 cnt = 0; int i; int calculation_angle_cnt = 0; static float angle = 0; //避障的角度 static float last_angle = 0; // u16 mini_distance = 65535; static u8 data_count = 0; //用于滤除一写噪点的计数变量 //需要找出跟随的那个点的角度 for(i = 0; i < 1152; i++) { if((0<Dataprocess[i].distance)&&(Dataprocess[i].distance<Follow_Distance)) { calculation_angle_cnt++; if(Dataprocess[i].distance<mini_distance) { mini_distance = Dataprocess[i].distance; angle = Dataprocess[i].angle; } } } if(angle > 180) //0--360度转换成0--180;-180--0(顺时针) angle -= 360; if((angle-last_angle > 10)||(angle-last_angle < -10)) //做一定消抖,波动大于10度的需要做判断 { if(++data_count > 30) //连续30次采集到的值(300ms后)和上次的比大于10度,此时才是认为是有效值 { data_count = 0; last_angle = angle; } } else //波动小于10度的可以直接认为是有效值 { if(++data_count > 10) //连续10次采集到的值(100ms后),此时才是认为是有效值 { data_count = 0; last_angle = angle; } } if(calculation_angle_cnt < 8) //跟随距离小于8且当cnt>40的时候,认为在1600内没有跟随目标 { if(cnt < 40) cnt++; if(cnt >= 40) { Move_X = 0; Move_Z = 0; } } else { cnt = 0; if(Move_X > 0.06f || Move_X < -0.06f) //当Move_X有速度时,转向PID开始调整 { if(mini_distance < 700 && (last_angle > 60 || last_angle < -60)) { Move_Z = -0.0098f*last_angle; //当距离偏小且角度差距过大直接快速转向 } else { Move_Z = -Follow_Turn_PID(last_angle,0); //转向PID,车头永远对着跟随物品 } } else { Move_Z = 0; } if(angle>150 || angle<-150) //如果小车在后方60°需要反方向运动以及快速转弯 { Move_X = -Distance_Adjust_PID(mini_distance, Keep_Follow_Distance); Move_Z = -0.0098f*last_angle; } else { Move_X = Distance_Adjust_PID(mini_distance, Keep_Follow_Distance); //保持距离保持在500mm } Move_X = target_limit_float(Move_X,-amplitude_limiting,amplitude_limiting); //对前进速度限幅 } Drive_Motor(Move_X,Move_Y,Move_Z); } /************************************************************************** 函数功能:小车走直线模式 入口参数:无 返回 值:无 **************************************************************************/ void Lidar_along_wall(void) { static int determine; static u32 target_distance=0; u32 distance; u8 data_count = 0; //用于滤除一写噪点的计数变量 Move_X = forward_velocity; //初始速度 for(int j=0;j<1152;j++) //225 { if(Dataprocess[j].angle>268 && Dataprocess[j].angle<272) //取雷达的4度的点 { if(determine300||Dataprocess[i].angle<60) {//车前避障角度 300°-60° if(0<Dataprocess[i].distance&&Dataprocess[i].distance<avoid_distance) { avoid_point_count++;//需要避障的点数 if(Dataprocess[i].angle<60) { right_angle_sum+=Dataprocess[i].angle; } else if(Dataprocess[i].angle>300) { left_angle_sum+=(Dataprocess[i].angle-360);//300°到359°转化为-60°到-1° } if(Dataprocess[i].distance<Avoid_Min_Distance) {//小于最小避障距离 mini_distance_count++; } } } } if(*Vx>0&&avoid_point_count>8){//需要避障 if(mini_distance_count>=8)//距离小于最小转弯距离要求 { *Vx=*Vx*-0.75f;//后退 *Vz=0; } else { if(right_angle_sum+left_angle_sum>0) {//往左避让 *Vx=*Vx*0.5f;//避障的X轴速度是前进速度的一半 if(Car_Mode==Akm_Car){ *Vz=(PI/2)*4/9; }else{ *Vz=1; } }else{//往右避让 *Vx=*Vx*0.5f;//避障的X轴速度是前进速度的一半 if(Car_Mode==Akm_Car){ *Vz=(-PI/2)*4/9; }else{ *Vz=-1; } } } } } /************************************************************************** 函数功能:限制PWM赋值 入口参数:无 返回 值:无 **************************************************************************/ void Xianfu_Pwm(void) { int Amplitude_H1=1200, Amplitude_L1=300; //舵机脉宽极限值,即限制舵机转角13.5°-256.5°中心位置为750,运动范围为±450,(750-250)/90=5.5,5.5*80=450 int Amplitude_H2=1200, Amplitude_L2=300; //舵机脉宽极限值,即限制舵机转角9°-171°,中心位置为750,运动范围为±450,(750-250)/90=5.5,5.5*80=450 if(Target1<Amplitude_L1) Target1=Amplitude_L1; if(Target1>Amplitude_H1) Target1=Amplitude_H1; if(Target2<Amplitude_L2) Target2=Amplitude_L2; if(Target2>Amplitude_H2) Target2=Amplitude_H2; } /************************************************************************* 函数功能:位置式PID控制器 入口参数:编码器测量位置信息,目标位置 返回 值:电机PWM增量 **************************************************************************/ float Position_PID_1(float Position,float Target) { //增量输出 static float Bias,Pwm,Integral_bias,Last_Bias; Bias=Target-Position; //计算偏差 Integral_bias+=Bias; //求出偏差的积分 Pwm=Position_KP*Bias/100+Position_KI*Integral_bias/100+Position_KD*(Bias-Last_Bias)/100; //位置式PID控制器 Last_Bias=Bias; //保存上一次偏差 return Pwm; } /************************************************************************* 函数功能:位置式PID控制器 入口参数:编码器测量位置信息,目标位置 返回 值:电机PWM增量 **************************************************************************/ float Position_PID_2(float Position,float Target) { //增量输出 static float Bias,Pwm,Integral_bias,Last_Bias; Bias=Target-Position; //计算偏差 Integral_bias+=Bias; //求出偏差的积分 Pwm=Position_KP*Bias/100+Position_KI*Integral_bias/100+Position_KD*(Bias-Last_Bias)/100; //位置式PID控制器 Last_Bias=Bias; //保存上一次偏差 return Pwm; } /************************************************************************** 函数功能:限制速度 入口参数:无 返回 值:无 **************************************************************************/ void Xianfu_Velocity(void) { int Amplitude_H=Speed, Amplitude_L=-Speed; if(Velocity1<Amplitude_L) Velocity1=Amplitude_L; if(Velocity1>Amplitude_H) Velocity1=Amplitude_H; if(Velocity2<Amplitude_L) Velocity2=Amplitude_L; if(Velocity2>Amplitude_H) Velocity2=Amplitude_H; } /************************************************************************** 函数功能:赋值给PWM寄存器,并且判断转向 入口参数:左轮PWM、右轮PWM 返回 值:无 **************************************************************************/ void Set_Pwm_PTZ(float velocity1,float velocity2) { Position1+=velocity1; //速度的积分,得到舵机的位置 Position2+=velocity2; //赋值给STM32的寄存器 // TIM12->CCR1=Position1; //GPIOA8,云台舵机 // TIM12->CCR2=Position2; //GPIOA11,外臂舵机 TIM8->CCR1=Position1; //GPIOC6,云台舵机 TIM8->CCR2=Position2; //GPIOC7,外臂舵机 }看不懂啊,逐行解释

下面是我写的代码,你分析一下什么原因#include "adc.h" #include "./SYSTEM/delay/delay.h" #include <math.h> ADC_HandleTypeDef g_adc_handle; /* ADC句柄 */ ///* ADC初始化函数,本函数支持ADC1/ADC2任意通道, 但是不支持ADC3 */ //void adc_init(void) //{ // g_adc_handle.Instance = ADC_ADCX; /* 选择哪个ADC */ // g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; /* 数据对齐方式:右对齐 */ // g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; /* 非扫描模式,仅用到一个通道 */ // g_adc_handle.Init.ContinuousConvMode = DISABLE; /* 关闭连续转换模式 */ // g_adc_handle.Init.NbrOfConversion = 1; /* 赋值范围是1~16,本实验用到1个规则通道序列 */ // g_adc_handle.Init.DiscontinuousConvMode = DISABLE; /* 禁止规则通道组间断模式 */ // g_adc_handle.Init.NbrOfDiscConversion = 0; /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */ // g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* 触发转换方式:软件触发 */ // HAL_ADC_Init(&g_adc_handle); /* 初始化 */ // HAL_ADCEx_Calibration_Start(&g_adc_handle); /* 校准ADC */ //} ///* ADC底层驱动,引脚配置,时钟使能 */ //void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc) //{ // if(hadc->Instance == ADC_ADCX) // { // GPIO_InitTypeDef gpio_init_struct; // RCC_PeriphCLKInitTypeDef adc_clk_init = {0}; // // ADC_ADCX_CHY_CLK_ENABLE(); /* 使能ADCx时钟 */ // ADC_ADCX_CHY_GPIO_CLK_ENABLE(); /* 开启GPIO时钟 */ // /* 设置ADC时钟 */ // adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; /* ADC外设时钟 */ // adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; /* 分频因子6时钟为72M/6=12MHz */ // HAL_RCCEx_PeriphCLKConfig(&adc_clk_init); /* 设置ADC时钟 */ // /* 设置AD采集通道对应IO引脚工作模式 */ // gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN; /* ADC通道IO引脚 */ // gpio_init_struct.Mode = GPIO_MODE_ANALOG; /* 模拟 */ // HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct); // } //} ///** // * @brief 设置ADC通道采样时间 // * @param adcx : adc句柄指针,ADC_HandleTypeDef // * @param ch : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17 // * @param stime: 采样时间 0~7, 对应关系为: // * @arg ADC_SAMPLETIME_1CYCLE_5, 1.5个ADC时钟周期 ADC_SAMPLETIME_7CYCLES_5, 7.5个ADC时钟周期 // * @arg ADC_SAMPLETIME_13CYCLES_5, 13.5个ADC时钟周期 ADC_SAMPLETIME_28CYCLES_5, 28.5个ADC时钟周期 // * @arg ADC_SAMPLETIME_41CYCLES_5, 41.5个ADC时钟周期 ADC_SAMPLETIME_55CYCLES_5, 55.5个ADC时钟周期 // * @arg ADC_SAMPLETIME_71CYCLES_5, 71.5个ADC时钟周期 ADC_SAMPLETIME_239CYCLES_5, 239.5个ADC时钟周期 // * @param rank: 多通道采集时需要设置的采集编号, // 假设你定义channle1的rank=1,channle2 的rank=2, // 那么对应你在DMA缓存空间的变量数组AdcDMA[0] 就i是channle1的转换结果,AdcDMA[1]就是通道2的转换结果。 // 单通道DMA设置为 ADC_REGULAR_RANK_1 // * @arg 编号1~16:ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16 // * @retval 无 // */ //void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime) //{ // ADC_ChannelConfTypeDef adc_ch_conf; // // adc_ch_conf.Channel = ch; /* 通道 */ // adc_ch_conf.Rank = rank; /* 序列 */ // adc_ch_conf.SamplingTime = stime; /* 采样时间 */ // HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf); /* 通道配置 */ //} ///** // * @brief 获得ADC转换后的结果 // * @param ch: 通道值 0~17,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_17 // * @retval 无 // */ //uint32_t adc_get_result(uint32_t ch) //{ // adc_channel_set(&g_adc_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5); /* 设置通道,序列和采样时间 */ // HAL_ADC_Start(&g_adc_handle); /* 开启ADC */ // HAL_ADC_PollForConversion(&g_adc_handle, 20); /* 轮询转换 */ // return (uint16_t)HAL_ADC_GetValue(&g_adc_handle); /* 返回最近一次ADC1规则组的转换结果 */ //} ///** // * @brief 获取通道ch的转换值,取times次,然后平均 // * @param ch : 通道号, 0~17 // * @param times : 获取次数 // * @retval 通道ch的times次转换结果平均值 // */ //uint32_t adc_get_result_average(uint32_t ch, uint8_t times) //{ // uint32_t temp_val = 0; // uint8_t t; // for (t = 0; t < times; t++) /* 获取times次数据 */ // { // temp_val += adc_get_result(ch); // delay_ms(5); // } // return temp_val / times; /* 返回平均值 */ //} /*************************单通道ADC采集(DMA读取)实验和多通道ADC采集(DMA读取)实验公用代码*******************************/ DMA_HandleTypeDef g_dma_adc_handle = {0}; // 定义要搬运ADC数据的DMA句柄 ADC_HandleTypeDef g_adc_dma_handle = {0}; // 定义ADC(DMA读取)句柄 uint8_t g_adc_dma_sta = 0; // DMA传输状态标志, 0,未完成; 1, 已完成 DMA_HandleTypeDef g_dma_nch_adc_handle = {0}; /* 定义要搬运ADC多通道数据的DMA句柄 */ ADC_HandleTypeDef g_adc_nch_dma_handle = {0}; /* 定义ADC(多通道DMA读取)句柄 */ /** * @brief ADC N通道(6通道) DMA读取 初始化函数 * @note 本函数还是使用adc_init对ADC进行大部分配置,有差异的地方再单独配置 * 另外,由于本函数用到了6个通道, 宏定义会比较多内容, 因此,本函数就不采用宏定义的方式来修改通道了, * 直接在本函数里面修改, 这里我们默认使用PA0~PA5这6个通道. * * 注意: 本函数还是使用 ADC_ADCX(默认=ADC1) 和 ADC_ADCX_DMACx( DMA1_Channel1 ) 及其相关定义 * 不要乱修改adc.h里面的这两部分内容, 必须在理解原理的基础上进行修改, 否则可能导致无法正常使用. * * @param mar : 存储器地址 * @retval 无 */ void adc_nch_dma_init(uint32_t mar) { GPIO_InitTypeDef gpio_init_struct; RCC_PeriphCLKInitTypeDef adc_clk_init = {0}; ADC_ChannelConfTypeDef adc_ch_conf = {0}; ADC_ADCX_CHY_CLK_ENABLE(); /* 使能ADCx时钟 */ __HAL_RCC_GPIOA_CLK_ENABLE(); /* 开启GPIOA时钟 */ if ((uint32_t)ADC_ADCX_DMACx > (uint32_t)DMA1_Channel7) /* 大于DMA1_Channel7, 则为DMA2的通道了 */ { __HAL_RCC_DMA2_CLK_ENABLE(); /* DMA2时钟使能 */ } else { __HAL_RCC_DMA1_CLK_ENABLE(); /* DMA1时钟使能 */ } /* 设置ADC时钟 */ adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; /* ADC外设时钟 */ adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; /* 分频因子6时钟为72M/6=12MHz */ HAL_RCCEx_PeriphCLKConfig(&adc_clk_init); /* 设置ADC时钟 */ /* 设置ADC1通道0~5对应的IO口模拟输入 AD采集引脚模式设置,模拟输入 PA1对应 ADC1_IN1 PA2对应 ADC1_IN2 PA3对应 ADC1_IN3 */ gpio_init_struct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3; /* GPIOA1-3 */ gpio_init_struct.Mode = GPIO_MODE_ANALOG; /* 模拟 */ HAL_GPIO_Init(GPIOA, &gpio_init_struct); /* 初始化DMA */ g_dma_nch_adc_handle.Instance = ADC_ADCX_DMACx; /* 设置DMA通道 */ g_dma_nch_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY; /* 从外设到存储器模式 */ g_dma_nch_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE; /* 外设非增量模式 */ g_dma_nch_adc_handle.Init.MemInc = DMA_MINC_ENABLE; /* 存储器增量模式 */ g_dma_nch_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* 外设数据长度:16位 */ g_dma_nch_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; /* 存储器数据长度:16位 */ g_dma_nch_adc_handle.Init.Mode = DMA_NORMAL; /* 外设流控模式 */ g_dma_nch_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM; /* 中等优先级 */ HAL_DMA_Init(&g_dma_nch_adc_handle); __HAL_LINKDMA(&g_adc_nch_dma_handle, DMA_Handle, g_dma_nch_adc_handle); /* 将DMA与adc联系起来 */ /* 初始化ADC */ g_adc_nch_dma_handle.Instance = ADC_ADCX; /* 选择哪个ADC */ g_adc_nch_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; /* 数据对齐方式:右对齐 */ g_adc_nch_dma_handle.Init.ScanConvMode = ADC_SCAN_ENABLE; /* 使能扫描模式 */ g_adc_nch_dma_handle.Init.ContinuousConvMode = ENABLE; /* 使能连续转换 */ g_adc_nch_dma_handle.Init.NbrOfConversion = 3; /* 赋值范围是1~16,本实验用到3个规则通道序列 */ g_adc_nch_dma_handle.Init.DiscontinuousConvMode = DISABLE; /* 禁止规则通道组间断模式 */ g_adc_nch_dma_handle.Init.NbrOfDiscConversion = 0; /* 配置间断模式的规则通道个数,禁止规则通道组间断模式后,此参数忽略 */ g_adc_nch_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* 软件触发 */ HAL_ADC_Init(&g_adc_nch_dma_handle); /* 初始化 */ HAL_ADCEx_Calibration_Start(&g_adc_nch_dma_handle); /* 校准ADC */ /* 配置ADC通道 */ adc_ch_conf.Channel = ADC_CHANNEL_1; /* 配置使用的ADC通道 */ adc_ch_conf.Rank = ADC_REGULAR_RANK_1; /* 采样序列里的第1个 */ adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5 ; /* 采样时间,设置最大采样周期:239.5个ADC周期 */ HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf); /* 通道配置 */ adc_ch_conf.Channel = ADC_CHANNEL_2; /* 配置使用的ADC通道 */ adc_ch_conf.Rank = ADC_REGULAR_RANK_2; /* 采样序列里的第2个 */ adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5 ; /* 采样时间,设置最大采样周期:239.5个ADC周期 */ HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf); /* 配置ADC通道 */ adc_ch_conf.Channel = ADC_CHANNEL_3; /* 配置使用的ADC通道 */ adc_ch_conf.Rank = ADC_REGULAR_RANK_3; /* 采样序列里的第3个 */ adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5 ; /* 采样时间,设置最大采样周期:239.5个ADC周期 */ HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf); /* 配置ADC通道 */ /* 配置DMA数据流请求中断优先级 */ HAL_NVIC_SetPriority(ADC_ADCX_DMACx_IRQn, 3, 3); HAL_NVIC_EnableIRQ(ADC_ADCX_DMACx_IRQn); HAL_DMA_Start_IT(&g_dma_nch_adc_handle, (uint32_t)&ADC1->DR, mar, 0); /* 启动DMA,并开启中断 */ HAL_ADC_Start_DMA(&g_adc_nch_dma_handle, &mar, 0); /* 开启ADC,通过DMA传输结果 */ } /*************************单通道ADC采集(DMA读取)实验和多通道ADC采集(DMA读取)实验公用代码*******************************/ /** * @brief 使能一次ADC DMA传输 * @note 该函数用寄存器来操作,防止用HAL库操作对其他参数有修改,也为了兼容性 * @param ndtr: DMA传输的次数 * @retval 无 */ void adc_dma_enable(uint16_t cndtr) { ADC_ADCX->CR2 &= ~(1 << 0); /* 先关闭ADC */ ADC_ADCX_DMACx->CCR &= ~(1 << 0); /* 关闭DMA传输 */ while (ADC_ADCX_DMACx->CCR & (1 << 0)); /* 确保DMA可以被设置 */ ADC_ADCX_DMACx->CNDTR = cndtr; /* DMA传输数据量 */ ADC_ADCX_DMACx->CCR |= 1 << 0; /* 开启DMA传输 */ ADC_ADCX->CR2 |= 1 << 0; /* 重新启动ADC */ ADC_ADCX->CR2 |= 1 << 22; /* 启动规则转换通道 */ } /** * @brief ADC DMA采集中断服务函数 * @param 无 * @retval 无 */ void ADC_ADCX_DMACx_IRQHandler(void) { if (ADC_ADCX_DMACx_IS_TC()) { g_adc_dma_sta = 1; /* 标记DMA传输完成 */ ADC_ADCX_DMACx_CLR_TC(); /* 清除DMA1 数据流7 传输完成中断 */ } },main.c代码#include "./SYSTEM/sys/sys.h" #include "./SYSTEM/usart/usart.h" #include "./SYSTEM/delay/delay.h" #include "./BSP/LED/led.h" #include "./BSP/DMA/dma.h" #include "./BSP/KEY/key.h" #include "./BSP/ADC/adc.h" #include "OLED.h" // 重新排序全局变量定义,确保阈值变量在ADC缓冲区之前 volatile float MQ7_ALARM_THRESHOLD = 7.0; // 添加volatile防止优化 volatile float MQ8_ALARM_THRESHOLD = 20.0; volatile float MP2_ALARM_THRESHOLD = 15.0; // 添加全局变量存储基线值 float baseline_values[3] = {0}; extern const uint8_t HZ_zhong[]; // 添加内存保护屏障 #define ADC_DMA_BUF_SIZE (3 * 50) /* ADC DMA缓冲区大小 */ uint32_t __attribute__((aligned(4))) memory_barrier[4]; // 内存屏障 static uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE]; /* ADC DMA缓冲区 */ extern uint8_t g_adc_dma_sta; /* DMA传输完成标志 */ // 汉字点阵数据 const uint8_t HZ_guo[] = { 0x00,0xFE,0xFE,0x12,0x92,0x92,0x92,0xF2, 0xF2,0x92,0x92,0x92,0x12,0xFE,0xFE,0x00, 0x00,0xFF,0xFF,0x48,0x48,0x48,0x48,0x4F, 0x4F,0x4A,0x4E,0x4C,0x48,0xFF,0xFF,0x00 }; const uint8_t HZ_nan[] = { 0x04,0xE4,0xE4,0x24, 0x64,0xE4,0xA4,0x3F, 0x3F,0xA4,0xE4,0x64, 0x24,0xE4,0xE4,0x04, 0x00,0xFF,0xFF,0x08, 0x09,0x09,0x09,0x7F, 0x7F,0x09,0x09,0x49, 0xC8,0xFF,0x7F,0x00,/*"南",0*/ }; const uint8_t HZ_fang[] = { 0x08,0x08,0x08,0x08, 0x08,0xF8,0xF9,0x8F, 0x8E,0x88,0x88,0x88, 0x88,0x08,0x08,0x08, 0x00,0x80,0xC0,0x60, 0x38,0x1F,0x07,0x00, 0x40,0xC0,0xC0,0x7F, 0x3F,0x00,0x00,0x00,/*"方",0*/ }; const uint8_t HZ_dian[] = { 0x00,0x00,0xF8,0xF8, 0x88,0x88,0x88,0xFF, 0xFF,0x88,0x88,0x88, 0xF8,0xF8,0x00,0x00, 0x00,0x00,0x1F,0x1F, 0x08,0x08,0x08,0x7F, 0xFF,0x88,0x88,0x88, 0x9F,0x9F,0xF0,0xF0,/*"电",0*/ }; const uint8_t HZ_wang[] = { 0x00,0xFE,0xFE,0x22, 0x62,0xC2,0xF2,0x72, 0x22,0x62,0xC2,0xF2, 0x72,0xFE,0xFE,0x00, 0x00,0xFF,0xFF,0x18, 0x0E,0x07,0x0F,0x1E, 0x18,0x0E,0x07,0x4F, 0xCE,0xFF,0x7F,0x00,/*"网",0*/ }; ////////////////////////////////////// 按键处理优化 /////////////////////////////////// int selected_sensor = 0; // 当前选择的传感器 // 按键状态结构体 typedef struct { uint32_t last_time; // 上次检测时间 uint8_t last_state; // 上次状态 uint8_t pressed; // 按下标志 } KeyState; KeyState xz_key = {0, 1, 0}; KeyState key0_key = {0, 1, 0}; KeyState key1_key = {0, 1, 0}; // 更新阈值函数(带范围限制) void update_threshold(float delta) { switch (selected_sensor) { case 0: MQ7_ALARM_THRESHOLD += delta; if (MQ7_ALARM_THRESHOLD < 0) MQ7_ALARM_THRESHOLD = 0; if (MQ7_ALARM_THRESHOLD > 100) MQ7_ALARM_THRESHOLD = 100; break; case 1: MQ8_ALARM_THRESHOLD += delta; if (MQ8_ALARM_THRESHOLD < 0) MQ8_ALARM_THRESHOLD = 0; if (MQ8_ALARM_THRESHOLD > 100) MQ8_ALARM_THRESHOLD = 100; break; case 2: MP2_ALARM_THRESHOLD += delta; if (MP2_ALARM_THRESHOLD < 0) MP2_ALARM_THRESHOLD = 0; if (MP2_ALARM_THRESHOLD > 100) MP2_ALARM_THRESHOLD = 100; break; } } // 更新按键状态 void update_key_state(KeyState *key, GPIO_PinState current_state) { uint32_t now = HAL_GetTick(); uint8_t current_pin_state = (current_state == GPIO_PIN_SET) ? 1 : 0; // 检测下降沿(按键按下) if (key->last_state == 1 && current_pin_state == 0) { if ((now - key->last_time) > 20) { // 20ms消抖 key->pressed = 1; } } // 更新状态 key->last_state = current_pin_state; key->last_time = now; } // 处理按键逻辑 void process_keys(void) { // 更新按键状态 update_key_state(&xz_key, HAL_GPIO_ReadPin(XZ_GPIO_PORT, XZ_GPIO_PIN)); update_key_state(&key0_key, HAL_GPIO_ReadPin(KEY0_GPIO_PORT, KEY0_GPIO_PIN)); update_key_state(&key1_key, HAL_GPIO_ReadPin(KEY1_GPIO_PORT, KEY1_GPIO_PIN)); // 处理XZ按键(选择传感器) if (xz_key.pressed) { selected_sensor = (selected_sensor + 1) % 3; xz_key.pressed = 0; } // 处理KEY0按键(增加阈值) if (key0_key.pressed) { update_threshold(1.0f); key0_key.pressed = 0; } // 处理KEY1按键(减少阈值) if (key1_key.pressed) { update_threshold(-1.0f); key1_key.pressed = 0; } } /////////////////////////////////////////////////////////////////////////////////// // 显示电压值(清除残留字符) void display_voltage_value(uint8_t x, uint8_t y, float voltage) { char buffer[10]; // 清除旧内容(5位宽度,包括小数点和两位小数) OLED_ShowString(x, y, " ", 8); // 显示新值 snprintf(buffer, sizeof(buffer), "%.0f", voltage); OLED_ShowString(x, y, buffer, 8); } // 显示阈值值(清除残留字符) void display_threshold_value(uint8_t x, uint8_t y, float threshold) { char buffer[10]; // 清除旧内容(4位宽度,包括小数点和一位小数) OLED_ShowString(x, y, " ", 8); // 显示新值 snprintf(buffer, sizeof(buffer), "%.1f", threshold); OLED_ShowString(x, y, buffer, 8); } int main(void) { // float mq7_concentration, mq8_concentration, mp2_concentration; HAL_Init(); sys_stm32_clock_init(RCC_PLL_MUL9); delay_init(72); usart_init(115200); led_init(); key_init(); OLED_Init(); OLED_Clear(); // 开机动画 OLED_ShowChinese(2, 16, HZ_zhong); OLED_ShowChinese(21, 16, HZ_guo); OLED_ShowChinese(40, 16, HZ_nan); OLED_ShowChinese(59, 16, HZ_fang); OLED_ShowChinese(78, 16, HZ_dian); OLED_ShowChinese(97, 16, HZ_wang); OLED_UpData(); delay_ms(1000); OLED_Clear(); // 添加变量用于存储每个通道的ADC值和电压 uint16_t adc_value[3] = {0}; float voltage[3] = {0.0f}; // 初始化ADC DMA adc_nch_dma_init((uint32_t)g_adc_dma_buf); adc_dma_enable(ADC_DMA_BUF_SIZE); // 显示初始静态文本 OLED_ShowString(1, 1, "CO:", 8); OLED_ShowString(1, 16, "CO2:", 8); OLED_ShowString(1, 32, "CH4:", 8); // 初始显示阈值(使用清除残留字符的函数) display_threshold_value(90, 1, MQ7_ALARM_THRESHOLD); display_threshold_value(90, 16, MQ8_ALARM_THRESHOLD); display_threshold_value(90, 32, MP2_ALARM_THRESHOLD); // 初始选择标记 selected_sensor = 0; OLED_ShowString(80, 1, "*", 8); // 初始选择第一个传感器 OLED_UpData(); delay_ms(100); while (1) { // 处理按键 process_keys(); // 更新选择标记 - 先清除所有标记 OLED_ShowString(80, 1, " ", 8); OLED_ShowString(80, 16, " ", 8); OLED_ShowString(80, 32, " ", 8); // 设置当前选中标记 switch (selected_sensor) { case 0: OLED_ShowString(80, 1, "*", 8); break; case 1: OLED_ShowString(80, 16, "*", 8); break; case 2: OLED_ShowString(80, 32, "*", 8); break; } if (g_adc_dma_sta == 1) { // 处理DMA缓冲区数据 for (uint8_t j = 0; j < 3; j++) { uint32_t sum = 0; // 计算该通道的平均值 for (uint16_t i = 0; i < (ADC_DMA_BUF_SIZE / 3); i++) { sum += g_adc_dma_buf[(3 * i) + j]; } // 存储平均值 adc_value[j] = sum / (ADC_DMA_BUF_SIZE / 3); switch(j) { case 0: display_voltage_value(35, 1, adc_value[0] ); break; case 1: display_voltage_value(35, 16, adc_value[1] ); break; case 2: display_voltage_value(35, 32, adc_value[2] ); break; } } display_threshold_value(90, 1, MQ7_ALARM_THRESHOLD); display_threshold_value(90, 16, MQ8_ALARM_THRESHOLD); display_threshold_value(90, 32, MP2_ALARM_THRESHOLD); g_adc_dma_sta = 0; adc_dma_enable(ADC_DMA_BUF_SIZE); } LED0_TOGGLE(); OLED_UpData(); delay_ms(10); } },用的是STM32F103的M48Z-M3的单片机开发板,出现了使用多通道ADC采集时给ADC采集引脚接上气体传感器后其数值变化浮动很大的情况,并且气体传感器在检测到对应气体时依旧我行我素地在那波动,而不是检测到气体后数值升高

<?xml version="1.0" encoding="utf-8"?> <layout xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="viewModel" type="com.tplink.tpmifi.viewmodel.SmsBoxViewModel" /> </data> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top" android:addStatesFromChildren="true" android:background="@drawable/toolbar_background" android:minHeight="@dimen/titlebar_height"> <ImageView android:id="@+id/title_left_icon" android:layout_width="@dimen/titlebar_action_image_size" android:layout_height="@dimen/titlebar_action_image_size" android:scaleType="centerInside" android:src="@drawable/arrow_back_white" android:visibility="visible" /> <com.tplink.design.text.TPTextView android:id="@+id/title_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginStart="@dimen/titlebar_title_name_margin_start" android:layout_toEndOf="@+id/title_left" android:layout_weight="1" android:fontFamily="sans-serif-medium" android:lines="1" android:textAlignment="gravity" android:textColor="@android:color/white" android:textSize="@dimen/tpds_all_text_size_18" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_bias="0" app:layout_constraintStart_toEndOf="@id/title_left" app:layout_constraintTop_toTopOf="parent" tools:text="Outbox" /> <com.tplink.design.text.TPTextView android:id="@+id/title_right_text1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginEnd="20dp" android:layout_toStartOf="@+id/title_right_text2" android:background="@drawable/btn_bg" android:gravity="center" android:lines="1" android:padding="@dimen/tpds_all_dp_16" android:text="@{viewModel.isAllItemSelected ? @string/common_deselect_all :@string/common_select_all }" android:textAlignment="gravity" android:textColor="@color/white" android:textSize="@dimen/tpds_all_text_size_16" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/title_right_text2" app:layout_constraintHorizontal_bias="1" app:layout_constraintStart_toEndOf="@id/title_name" app:layout_constraintTop_toTopOf="parent" app:visibleOrGone="@{viewModel.editing}" tools:visibility="visible" /> <com.tplink.design.text.TPTextView android:id="@+id/title_right_text2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_centerVertical="true" android:layout_marginEnd="16dp" android:background="@drawable/btn_bg" android:clickable="@{viewModel.totalMsgNum != 0 }" android:gravity="center" android:lines="1" android:padding="@dimen/tpds_all_dp_16" android:text="@{viewModel.editing ? @string/common_cancel : @string/common_edit }" android:textAlignment="gravity" android:textColor="@{viewModel.totalMsgNum != 0 ? @color/white : @color/title_bar_text_non_clickable }" android:textSize="@dimen/tpds_all_text_size_16" android:visibility="visible" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="1" app:layout_constraintStart_toEndOf="@id/title_right_text1" app:layout_constraintTop_toTopOf="parent" tools:text="@string/common_edit" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>

大家在看

recommend-type

自动控制原理课件

上海交大自动控制原理英文班课件,王伟和陈彩莲老师
recommend-type

毕业设计&课设-用Matlab编写的MUSIC算法实现毫米波OFDM信号的4D ISAC成像仿真.zip

matlab算法,工具源码,适合毕业设计、课程设计作业,所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答! matlab算法,工具源码,适合毕业设计、课程设计作业,所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答! matlab算法,工具源码,适合毕业设计、课程设计作业,所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答! matlab算法,工具源码,适合毕业设计、课程设计作业,所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答! matlab算法,工具源码,适合毕业设计、课程设计作业,所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答! matlab算法,工具源码,适合毕业设计、课程设计作业,所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随
recommend-type

B端产品经理必备:AntDesign3.9.x-Axure-20180903 Axure元件库

B端产品经理必备:AntDesign3.9.x_Axure_20180903 Axure元件库 包含布局、按钮、图标、面包屑、导航菜单、标签页、步骤条、表单、输入框、选择器、评分、上传、穿梭框、图标库、卡片、时间轴、树形控件、图表卡片、标签、提示、抽屉、警告、对话框、进度条、气泡确认框、信息板、列表页、详情页、结果页、个人页等全部组件原型
recommend-type

海康sdkC#封装及调用例子

C#封装了海康sdk 1.登录摄像头功能 2.登出摄像头功能 3.抓图 4.开始录制视频结束录制视频 5.布防 6.布防回调(移动侦测报警,门禁主机报警信息,门禁身份证刷卡信息,门禁通行人数信息) 7.撤消布防
recommend-type

system verilog for design 2nd edition

this book introduces how to model a hardware description code using system verilog effectively

最新推荐

recommend-type

输入框限制输入两位小数数字

资源下载链接为: https://pan.quark.cn/s/9648a1f24758 这是一个非常实用且易于上手的网页代码片段,它通过JavaScript实现了对输入框内容的精准校验,确保用户只能输入数字以及最多保留小数点后两位(该位数可根据实际需求灵活调整为任意n位)。与网上常见的校验方法相比,它具有显著的直观性和易用性,尤其适合初学者快速理解和应用。此代码片段封装在一个静态网页文件中,方便直接使用和修改。它经过精心设计,能够完美兼容Internet Explorer(IE)、Firefox(FF)、Chrome等主流浏览器,无需担心兼容性问题。用户可以根据自己的需求轻松调整小数点后保留的位数,操作简单便捷,大大提高了开发效率。
recommend-type

2022版微信自定义密码锁定程序保护隐私

标题《微信锁定程序2022,自定义密码锁》和描述“微信锁定程序2022,自定义密码锁,打开微信需要填写自己设定的密码,才可以查看微信信息和回复信息操作”提及了一个应用程序,该程序为微信用户提供了额外的安全层。以下是对该程序相关的知识点的详细说明: 1. 微信应用程序安全需求 微信作为一种广泛使用的即时通讯工具,其通讯内容涉及大量私人信息,因此用户对其隐私和安全性的需求日益增长。在这样的背景下,出现了第三方应用程序或工具,旨在增强微信的安全性和隐私性,例如我们讨论的“微信锁定程序2022”。 2. “自定义密码锁”功能 “自定义密码锁”是一项特定功能,允许用户通过设定个人密码来增强微信应用程序的安全性。这项功能要求用户在打开微信或尝试查看、回复微信信息时,必须先输入他们设置的密码。这样,即便手机丢失或被盗,未经授权的用户也无法轻易访问微信中的个人信息。 3. 实现自定义密码锁的技术手段 为了实现这种类型的锁定功能,开发人员可能会使用多种技术手段,包括但不限于: - 加密技术:对微信的数据进行加密,确保即使数据被截获,也无法在没有密钥的情况下读取。 - 应用程序层锁定:在软件层面添加一层权限管理,只允许通过验证的用户使用应用程序。 - 操作系统集成:与手机操作系统的安全功能进行集成,利用手机的生物识别技术或复杂的密码保护微信。 - 远程锁定与擦除:提供远程锁定或擦除微信数据的功能,以应对手机丢失或被盗的情况。 4. 微信锁定程序2022的潜在优势 - 增强隐私保护:防止他人未经授权访问微信账户中的对话和媒体文件。 - 防止数据泄露:在手机丢失或被盗的情况下,减少敏感信息泄露的风险。 - 保护未成年人:父母可以为孩子设定密码,控制孩子的微信使用。 - 为商业用途提供安全保障:在商务场合,微信锁定程序可以防止商业机密的泄露。 5. 使用微信锁定程序2022时需注意事项 - 正确的密码管理:用户需要记住设置的密码,并确保密码足够复杂,不易被破解。 - 避免频繁锁定:过于频繁地锁定和解锁可能会降低使用微信的便捷性。 - 兼容性和更新:确保微信锁定程序与当前使用的微信版本兼容,并定期更新以应对安全漏洞。 - 第三方应用风险:使用第三方应用程序可能带来安全风险,用户应从可信来源下载程序并了解其隐私政策。 6. 结语 微信锁定程序2022是一个创新的应用,它提供了附加的安全性措施来保护用户的微信账户。尽管在实施中可能会面临一定的挑战,但它为那些对隐私和安全有更高要求的用户提供了可行的解决方案。在应用此类程序时,用户应谨慎行事,确保其对应用程序的安全性和兼容性有所了解,并采取适当措施保护自己的安全密码。
recommend-type

【自动化脚本提速】:掌握序列生成的5种高效技巧

# 摘要 本文系统地阐述了自动化脚本提速的方法,重点介绍了序列生成的基础理论及其在脚本中的应用。通过探讨不同序列生成方法和高效技巧,本文旨在提高编程效率,优化自动化流程。同时,文中还涉及了高级技术,如嵌套循环、列表推导式和并行处理,这些技术不仅增加了序列生成的复杂性,同时也显著提升了效率。最后,本文通过综合案例分析,展示了一系列序列生成技巧的实际应用,并提出了优化建议和未来研究方向。 #
recommend-type

卷积神经网络中的分层!

<think>我们正在处理一个关于卷积神经网络(CNN)层级结构的问题。用户希望了解CNN的层级结构及其功能。根据提供的引用内容,我们可以整理出以下信息: 1. 引用[1]和[2]指出,一个完整的卷积神经网络通常包括以下层级: - 数据输入层(Input layer) - 卷积计算层(CONV layer) - ReLU激励层(ReLU layer) - 池化层(Pooling layer) - 全连接层(FC layer) - (可能还有)Batch Normalization层 2. 引用[2]详细说明了各层的作用: - 数据输入层:对原始图像
recommend-type

MXNet预训练模型介绍:arcface_r100_v1与retinaface-R50

根据提供的文件信息,我们可以从中提取出关于MXNet深度学习框架、人脸识别技术以及具体预训练模型的知识点。下面将详细说明这些内容。 ### MXNet 深度学习框架 MXNet是一个开源的深度学习框架,由Apache软件基金会支持,它在设计上旨在支持高效、灵活地进行大规模的深度学习。MXNet支持多种编程语言,并且可以部署在不同的设备上,从个人电脑到云服务器集群。它提供高效的多GPU和分布式计算支持,并且具备自动微分机制,允许开发者以声明性的方式表达神经网络模型的定义,并高效地进行训练和推理。 MXNet的一些关键特性包括: 1. **多语言API支持**:MXNet支持Python、Scala、Julia、C++等语言,方便不同背景的开发者使用。 2. **灵活的计算图**:MXNet拥有动态计算图(imperative programming)和静态计算图(symbolic programming)两种编程模型,可以满足不同类型的深度学习任务。 3. **高效的性能**:MXNet优化了底层计算,支持GPU加速,并且在多GPU环境下也进行了性能优化。 4. **自动并行计算**:MXNet可以自动将计算任务分配到CPU和GPU,无需开发者手动介入。 5. **扩展性**:MXNet社区活跃,提供了大量的预训练模型和辅助工具,方便研究人员和开发者在现有工作基础上进行扩展和创新。 ### 人脸识别技术 人脸识别技术是一种基于人的脸部特征信息进行身份识别的生物识别技术,广泛应用于安防、监控、支付验证等领域。该技术通常分为人脸检测(Face Detection)、特征提取(Feature Extraction)和特征匹配(Feature Matching)三个步骤。 1. **人脸检测**:定位出图像中人脸的位置,通常通过深度学习模型实现,如R-CNN、YOLO或SSD等。 2. **特征提取**:从检测到的人脸区域中提取关键的特征信息,这是识别和比较不同人脸的关键步骤。 3. **特征匹配**:将提取的特征与数据库中已有的人脸特征进行比较,得出最相似的人脸特征,从而完成身份验证。 ### 预训练模型 预训练模型是在大量数据上预先训练好的深度学习模型,可以通过迁移学习的方式应用到新的任务上。预训练模型的优点在于可以缩短训练时间,并且在标注数据较少的新任务上也能获得较好的性能。 #### arcface_r100_v1 arcface_r100_v1是一个使用ArcFace损失函数训练的人脸识别模型,基于ResNet-100架构。ArcFace是一种流行的深度学习人脸识别方法,它在损失函数层面上增强类间的区分度。在ArcFace中,通过引入角度余弦的特征分离度,改善了传统的Softmax损失函数,让学习到的人脸特征更加具有鉴别力。 ArcFace的模型文件包括: - model-0000.params: 这是模型权重参数文件。 - model-symbol.json: 这是包含网络结构定义的JSON文件。 #### retinaface-R50 retinaface-R50是基于ResNet-50架构的人脸检测模型,使用RetinaFace框架训练而成。RetinaFace是为了解决传统人脸检测模型在面对小尺寸、遮挡、模糊等复杂情况时识别准确度不高的问题而设计的。它采用一种基于多尺度的金字塔网络结构,能有效处理不同尺度的人脸,并且在特征提取时采用了一种高效的特征融合策略。 Retinaface-R50的模型文件包括: - R50-0000.params: 这是模型权重参数文件。 - R50-symbol.json: 这是包含网络结构定义的JSON文件。 ### 总结 从给定的文件信息中,我们可以看出这些预训练模型是基于MXNet深度学习框架开发的,具有专门针对人脸识别任务的优化。ArcFace模型通过增强特征的区分度,而Retinaface模型通过多尺度处理和高效的特征融合,都展示了在人脸检测和识别方面的先进技术。开发者可以利用这些预训练模型,结合MXNet提供的高级API,快速构建并部署自己的人脸识别解决方案。
recommend-type

【文本处理黑科技】:Shell脚本中序列和数组的高级应用

# 摘要 本文系统地介绍了Shell脚本中序列与数组的基础知识、操作技巧以及在实际应用中的优化方法。第一章和第二章分别对序列和数组的基础知识进行了阐述,重点讲解了序列的生成、操作、文本处理和数组的基本操作与高级特性。第三章探讨了数组在数据处理和内存管理中的性能优势。第四章结合实际案例,展示了序列与数组在日志文件分析、报告生成和配置文件管理
recommend-type

数据库用来干啥的

<think>首先,用户的问题是:“我想了解数据库的用途 请问数据库的主要用途是什么”。这翻译成英文是:“I want to understand the purpose of databases. What are the main uses of databases?” 从系统级指令中,我需要: - 所有行内数学表达式使用 $...$ 格式。 - 独立公式使用 $$...$$ 格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如 [^1]。 - 结构清晰,帮助用户逐步解决问题。 - 回答真实可
recommend-type

微软文字转语音软件发布,支持多国语言高精度识别

微软文字转语音V1.0.zip这个文件包含了一个软件程序,该程序能够实现文字到语音(Text-to-Speech,TTS)的转换,同时也提供了从音频文件到文字的转换功能。下面详细介绍这一技术及其应用的知识点。 首先,文字到语音转换技术,通常称为TTS技术,是计算机科学中的一个重要分支。TTS系统主要由三个关键部分构成:文本分析模块、语音合成模块和声音输出模块。文本分析模块负责理解输入文本的结构和含义,包括分词、词性标注、句法分析、语义分析等。语音合成模块则负责根据文本分析的结果,合成出自然的语音波形。声音输出模块则是将合成的语音波形转换为可听的声音。 微软文字转语音软件具有以下功能和特点: 1. 智能文字和语音互转:该软件能够将用户输入的文字信息转换成自然流畅的语音输出,同时也能将音频文件中的对话转换成文字文本。这种双向转换功能对于多种场景有着重要的应用价值,比如辅助视障人士、提供语音交互界面、制作多语种的语音内容等。 2. 高精度识别各国语言:软件支持高精度的语言识别功能,能处理多种语言的文本或音频。不同语言有不同的语法结构和发音特点,因此支持多语言识别需要对每一种语言都进行深入的研究和算法优化,以确保转换结果的准确性和自然度。 3. 一键拖拽,批量完成:该软件提供简便的操作界面,用户可以通过简单的拖拽动作将需要转换的文本或音频文件直接加入到软件中,进行批量处理。这种操作方式极大地方便了用户,提高了工作效率,尤其在处理大量数据时优势更加明显。 4. 各种音频格式任意选择:用户可以根据需要选择输出的音频格式,比如常见的MP3、WAV等,以便适用于不同的播放设备或播放环境。不同音频格式有其特定的用途,例如MP3格式因为压缩比例高而被广泛用于网络传输和便携式设备,而WAV格式则多用于专业的音频编辑和制作。 软件包中的“resources”文件夹可能包含了支持软件运行的资源文件,如语音合成引擎所需的语音库、语言模型、字典等。而“转换结果”文件夹则可能是软件保存转换后文件的默认位置,用户可以在这里找到转换完成的文字或音频文件。 此外,软件包中的“微软文字转语音V1.0.exe”是一个可执行文件,用户通过运行该文件来启动软件,并使用其提供的各项转换功能。对于IT行业专业人士而言,了解这款软件背后的TTS技术原理和操作逻辑,可以更好地选择合适的解决方案,以满足特定的业务需求。 总结来说,微软文字转语音V1.0.zip中的软件是一款综合性的文字语音转换工具,具有高精度语言识别、高效批量处理、灵活音频格式选择等特点,可以应用于多种场景,提高信息的可访问性和交互性。
recommend-type

【Shell脚本必备】:创建序列的3种方法及高效用法

# 摘要 本文全面探讨了在Shell脚本中创建和优化序列生成的各种方法及其应用场景。首先介绍了序列生成的基本概念和使用基本命令创建序列的技巧,包括for循环、seq命令和算术表达式的应用。随后,深入分析了利用高级Shell特性如数组、复合命令和子shell、以及C风格的for循环来创建复杂序列的技术。文章还探讨了序列在文件批量处理、数据处理分析和自动化脚本中的高效应用。此外,为提升
recommend-type

mac烤机软件

<think>嗯,用户需要寻找适用于Mac的烤机测试软件。首先,我需要理解"烤机测试"的含义。这通常指的是压力测试,通过让电脑在高负载下运行,检测其稳定性和散热性能。用户可能刚买了新Mac,或者升级了系统,想测试机器在极端情况下的表现。 回顾提供的引用,其中提到了一些相关软件。引用[1]中的TechTool Pro被描述为Mac上强大的测试维护工具,能进行多种扫描,符合烤机测试的需求。引用[2]提到Mac mini的性能测试使用了FIO工具,但这是针对存储性能的,可能不全面。引用[4]提到xbench,但似乎已停止更新,可能不适合最新系统。 用户的需求是压力测试软件,需要覆盖CPU、GPU