Task

 

C#并行编程-Task(很重要的参考资料)

 

其实Task跟线程池ThreadPool的功能类似,不过写起来更为简单,直观。代码更简洁了,使用Task来进行操作。可以跟线程一样可以轻松的对执行的方法进行控制。

 

Task是.net4.0中的一个新特性,提供的功能非常强大。一个Task表示一个异步操作,Task提供了很多方法和属性,通过这些方法和属性能够对Task的执行进行控制,并且能够获得其状态信息。

Task的创建和执行都是独立的,因此可以对关联操作的执行拥有完全的控制权。

使用Parallel.For、Parallel.ForEach的循环迭代的并行执行,TPL会在后台创建System.Threading.Tasks.Task的实例。

使用Parallel.Invoke时,TPL也会创建与调用的委托数目一致的System.Threading.Tasks.Task的实例。

Task任务的状态

 

static void Main(string[] args)
{
    //Created:表示默认初始化任务,但是我们发现“工厂创建的”实例直接跳过。

    //WaitingToRun: 这种状态表示等待任务调度器分配线程给任务执行。

    //RanToCompletion:任务执行完毕。

    Task t1 = new Task(() => Program.ExecAction());
    Console.Write(t1.Status);//输出:Created
    t1.Start();
    Console.Write(t1.Status);//输出:WaitingToRun

    Task t2 = Task.Factory.StartNew(() => Program.ExecAction());
    Console.Write(t2.Status); //输出:WaitingToRun

    Task t3 = Task.Run(() => Program.ExecAction());
    Console.Write(t3.Status);//输出:WaitingToRun

    t3.ContinueWith(r => Console.Write(t3.Status));//输出:RanToCompletion

    
    Console.ReadKey();
}

~

 

 

namespace TestApp.Controllers
{
    public delegate string Mydelegate();
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            //特别注意,Task是接收不到返回值的,只有Task<TResult>才能接收到返回值(即:t1是没有Result属性的)
            //Task t1 = new Task(() => Console.WriteLine("启动了"));


            //启动一个任务的方法一:(需要调用Start方法启动任务)
            Task t1 = new Task(() => Test.ExecuteFun());
            //t1.RunSynchronously();//运行在主线程中,等同于直接运行:Test.ExecuteFun()因此,该Task不是运行在线程池中,而是运行在主线程中
            t1.Start(); //启动 Task,并将它安排到当前的 TaskScheduler 中执行。

            //启动一个任务的方法二:(就会立即启动任务)
            Task<string> t2 = Task.Factory.StartNew<string>(() => Test.GetUserName());
            //这里不能再次Start()了,因为StartNew就已经Start()了
            var userNameA = t2.Result; //得到“刘德华”

            //Task.Run的跟Task.Factory.StarNew和new Task相差不多,不同的是前两种是放进线程池立即执行,而Task.Run则是等线程池空闲后再执行。//启动一个任务的方法三:(就会立即启动任务)
            Task<string> t3 = Task.Run<string>(() => Test.GetUserName());//Task.Run方法是Task类中的静态方法,接受的参数是委托。返回值是为该Task对象。
            //这种方式是直接运行了Task,不像第一种方法那样还需要Start();			
            var userNameB = t3.Result; //得到“刘德华”
            
            //----------------------------------------------------//
            
            //Task类型公开了多个ContinueWith的重载。当另外一个task完成的时候,该方法会创建新的将被调度的task。该重载接受CancellationToken,TaskCreationOptions,和TaskScheduler,这些都对task的调度和执行提供了细粒度的控制。
                                       
            //TaskFactory类提供了ContinueWhenAll 和ContinueWhenAny方法。当提供的一系列的tasks中的所有或任何一个完成时,这些方法会创建一个即将被调度的新的task。有了ContinueWith,就有了对于调度的控制和任务的执行的支持。
                                       
            //一下表达式:表示在执行完t3这个任务后在执行Task.Run<int>(()=>Test.GetAge())这个任务【ContinueWith其实就相当于回调】
            var userAge = t3.ContinueWith(r => Task.Run<int>(() => Test.GetAge())).Result.Result; //得到“23”

            return View();
        }

    }

    public class Test
    {
        public static void ExecuteFun()
        {
            Console.WriteLine("哈哈");
        }
        public static int GetAge()
        {
            return 23;
        }
        public static string GetUserName()
        {
            return "刘德华";
        }
    }
}

~

 

public static Task<int> CallFuncAsync()
{
    //Func<int> c = () => { return 3; };
    //return Task.Run<int>(c); //这种写法,

    //return Task.Run<int>(() => Myfun());//或者下面这种写法
   
    return Task.Run<int>(() => { return 3; }); //或者这种写法

}

public static int Myfun()
{
    return 3;
} 

 

 

 

Task异常捕获

 

public class HomeController : Controller
{
    /// <summary>
    /// 调用 Task 的 Wait 方法时使用 try-catch 捕获异常:
    /// </summary>
    /// <returns></returns>
    public ActionResult Index()
    {
        Task t = Task.Run(() => TestException());
        try
        {
			//和线程不同,Task中抛出的异常可以捕获,但是也不是直接捕获,而是由调用Wait()方法或者访问Result属性的时候,由他们获得异常,将这个异常包装成AggregateException类型,再重新抛出捕获。 			
			
			//默认情况下,Task任务是由线程池线程异步执行。要知道Task任务的是否完成,可以通过task.IsCompleted属性获得,也可以使用task.Wait来等待Task完成。
			//Wait方法表示:等待Task任务执行完成(Wait会阻塞当前线程)
            t.Wait();
        }
        catch (Exception ex)
        {
            var a = ex.Message; //a的值为:发生一个或多个错误。
            var b = ex.GetBaseException(); //b的值为:Task异常测试
        }

        return View();
    }

    static void TestException()
    {
        throw new Exception("Task异常测试");
    }
}

2

 

public class HomeController : Controller
{
    /// <summary>
    /// ContinueWith的使用:
    /// </summary>
    /// <returns></returns>
    public ActionResult Index()
    {
        GetVal(6, 0); //0是不能作为被除数的,所以会引发错误
        return View();
    }
    private int Sum(int x, int y)
    {
        return x / y;
    }
    public void GetVal(int x, int y)
    {
        Task<string> t = Task.Run<string>(() => { return Sum(x, y).ToString(); });

        //ContinueWith方法是完成时异步执行的延续任务,这个延续认为是带一个参数的WriteLlog方法
        t.ContinueWith(r => { WriteLog("异常信息:" + t.Exception.InnerException.StackTrace); }, TaskContinuationOptions.OnlyOnFaulted); 
        //askContinuationOptions.OnlyOnFaulted表示:指定只应在延续任务前面的任务引发了未处理异常的情况下才安排延续任务。 此选项对多任务延续无效。【即:只有在发生异常的情况下才将异常信息记录到日志】
    }
    static void WriteLog(string logStr)
    {
        System.IO.File.AppendAllText(@"D:\Log.txt", logStr + "\r\n");
    }
}

3

class Program
{
    static void Main(string[] args)
    {
        GetStringAsync();
    }
    public static void GetStringAsync()
    {
        try
        {
            HttpClient hc = new HttpClient();
            var task1 = hc.GetStringAsync("http://www.1111.com");
            var task2 = hc.GetStringAsync("http://www.2222.com");
            var task3 = hc.GetStringAsync("http://www.3333.com");
            Task.WaitAll(task1, task2, task3);
            Console.WriteLine("执行成功");
        }
        //AggregateException类表示应用程序执行期间发生的一个或多个错误。(即:所有的内部异常)
        catch (AggregateException ae)
        {
            //InnerExceptions表示获取导致当前异常的System.Exception实例的只读集合。
            foreach (Exception item in ae.InnerExceptions)
            {
                Console.WriteLine(item.Message);
            }
            Console.ReadKey();
        }
    }
}

 

异步执行的延续任务:ContinueWith方法

 

 

 

public class HomeController : Controller
{
    /// <summary>
    /// ContinueWith的使用:
    /// </summary>
    /// <returns></returns>
    public ActionResult Index()
    {
        GetVal(5, 6);
        return View();
    }
    private int Sum(int x, int y)
    {
        return x + y;
    }
    public void GetVal(int x, int y)
    {
        Task<string> t = Task.Run<string>(() => { return Sum(x, y).ToString(); });
        //ContinueWith方法是完成时异步执行的延续任务,这个延续认为是带一个参数的WriteLlog方法
        t.ContinueWith(r => { WriteLlog(t.Result); }); //将Sum方法的返回结果写入日志
    }
    static void WriteLlog(string logStr)
    {
        System.IO.File.AppendAllText(@"D:\Log.txt", logStr + "/r/n");
    }
}

 

Task任务的取消

 

使用CancellationTokenSource对象需要与Task对象进行配合使用,Task会对当前运行的状态进行控制(这个不用我们关心是如何孔控制的)。而CancellationTokenSource则是外部对Task的控制,如取消、定时取消。

 

class Program
{
    static void Main(string[] args)
    {
        //通知 System.Threading.CancellationToken,告知其应被取消。
        var TokenSource = new CancellationTokenSource();

        //获取与此 System.Threading.CancellationTokenSource 关联的 System.Threading.CancellationToken。
        var Token = TokenSource.Token;

        var task = Task.Factory.StartNew(() =>
        {
            for (var i = 1; i < 1000; i++)
            {
                Thread.Sleep(100);
                //判断是否取消任务,取消为true;不取消为false
                if (Token.IsCancellationRequested)
                {
                    Console.Write("取消任务成功!");
                    return;
                }
            }
        },Token);

        //在这里还可以给token注册了一个方法,输出一条信息  用户输入0后,执行tokenSource.Cancel方法取消任务。这个取消任务执行后,会执行这个代码.
        Token.Register(() => Console.WriteLine("取消"));

		//等待用户输入
        var input = Console.ReadLine();

        if (Convert.ToInt32(input) == 0)
        {
            //如果输入了0,则取消这个任务;
			//一旦cancel被调用。task将会抛出OperationCanceledException来中断此任务的执行,最后将当前task的Status的IsCanceled属性设为true
            TokenSource.Cancel();
        }
        Console.ReadKey();
    }
}

 


 

多个 CancellationTokenSource 复合

 

在有多个CancellationTokenSource需要一起并行管理的时候,比如任意一个任务取消 则取消所有任务。我们不必去一个一个的去关闭,只需要将需要一起并行关闭的CancellationTokenSource组合起来就行了。如下代码所示,执行结果是跟上面的图一样的。我就不再上图了。

class Program
{
    //声明CancellationTokenSource对象
    static CancellationTokenSource c1 = new CancellationTokenSource();
    static CancellationTokenSource c2 = new CancellationTokenSource();
    static CancellationTokenSource c3 = new CancellationTokenSource();

    //使用多个CancellationTokenSource进行复合管理
    static CancellationTokenSource compositeCancel = CancellationTokenSource.CreateLinkedTokenSource(c1.Token, c2.Token, c3.Token);


    //主线程方法
    static void Main(string[] args)
    {
        var task = Task.Factory.StartNew(() =>
        {
            for (var i = 1; i < 1000; i++)
            {
                Thread.Sleep(100);
                //判断是否取消任务,取消为true;不取消为false
                if (compositeCancel.IsCancellationRequested)
                {
                    Console.Write("取消任务成功!");
                    return;
                }
            }
        }, compositeCancel.Token);


        //等待用户输入
        var input = Console.ReadLine();

        //如果输入了0,则取消这个任务;
        if (Convert.ToInt32(input) == 0)
        {

            //任意一个 CancellationTokenSource 取消任务,那么所有任务都会被取消。
            c1.Cancel();
        }
        Console.ReadKey();
    }
}

以上代码调用了c1.Cancel();触发了匿名方法中的compositeCancel.IsCancellationRequested为true,则取消了任务。所以我们在所有任务中判断复合的这个CancellationTokenSource对象即可。

 

 

 

 

 

Task的同步执行

 

class Program
{     
    static void Main(string[] args)
    {
        //RunSynchronously()表示同步执行,也就是阻塞式执行
        var t = new Task(() =>
         {
             Console.WriteLine("我是工作线程:TaskId={0}", Thread.CurrentThread.ManagedThreadId);
         });
        t.RunSynchronously();

        Console.WriteLine("我是主线程:TaskId={0}", Thread.CurrentThread.ManagedThreadId);

        Console.ReadKey();

        //我们可以看到使用了RunSynchronously()后,执行工作子任务的是主线程
    }      
}

 

 

 

 

 

 

 

 

 

 

 

void go_to_xy(float target_x, float target_y) 的函数发现执行时,只执行了第一阶段。考虑运用switch函数解决,可以使用别的方法#include <math.h> // 添加数学库用于fabs函数 #include "board.h" #include "my_key.h" #include "my_time.h" #include "ti_msp_dl_config.h" #include "oled.h" #define _USE_MATH_DEFINES #define WHEEL_BASE 80.0f #ifndef M_PI #define M_PI 3.14159265358979323846 #endif void BUZZY_OFF(void) { DL_GPIO_setPins(BUZZY_PORT, BUZZY_PIN_PIN); } void BUZZY_ON(void) { DL_GPIO_clearPins(BUZZY_PORT, BUZZY_PIN_PIN); } void refresh_oled(void); void key(void); void go_straight(int dis); void go_arc_ccd(hsu_time_t); void go_brc_ccd(hsu_time_t); void turn_90_degrees(int direction); void turn_in_place(float angle); void go_to_xy(float target_x, float target_y); void sound_light_alert(void); void show_task_now(void); u8 Car_Mode = Diff_Car; int Motor_Left, Motor_Right; // 电机PWM变量 应是Motor的 u8 PID_Send; // 延时和调参相关变量 float RC_Velocity = 200, RC_Turn_Velocity, Move_X, Move_Y, Move_Z, PS2_ON_Flag; // 遥控控制的速度 float Velocity_Left, Velocity_Right; // 车轮速度(mm/s) u16 test_num, show_cnt; float Voltage = 0; extern float Yaw; // 声明外部YAW角度变量 int64_t left_encoder = 0, right_encoder = 0; void SysTick_Handler(void) { hsu_time_systick_handler(); } typedef enum { BEGIN, T1, T2, T3, T4 } TaskState; typedef enum { STOP, GO_STRAIGHT, GO_CCD, TURN_IN_PLACE, TURN_90_DEGREES, WAIT_ALERT } DoingWhat; typedef struct __TASK_NAMESPACE { uint8_t is; uint8_t is_running; uint8_t finish; uint8_t sub_finish; uint8_t running_state; // 0: 停止, 1: 运行中 TaskState state; DoingWhat doing_what; float target; float vx; float vz; // 用于复杂任务 uint8_t sub_task_stage; // 子任务阶段 uint8_t lap_count; // 圈数计数 int64_t start_encoder; // 起始编码器值 uint32_t alert_start_time; // 声光提示开始时间 float start_yaw; // 起始YAW角度 float target_yaw_diff; // 目标YAW角度差 hsu_time_t ccd_end_time; float target_x; // 目标X坐标 (毫米) float target_y; // 目标Y坐标 (毫米) float current_x; // 当前X坐标 (毫米) float current_y; // 当前Y坐标 (毫米) int64_t rotation_start_left; // 旋转开始时左轮编码器值 int64_t rotation_start_right; // 旋转开始时右轮编码器值 float target_rotation; // 目标旋转量(弧度) } TaskNamespace; void reset_task_namespace(TaskNamespace *t) { t->is_running = 0; t->finish = 0; t->sub_finish = 0; t->state = BEGIN; t->doing_what = STOP; t->vx = 0; t->vz = 0; t->sub_task_stage = 0; t->lap_count = 0; t->start_encoder = left_encoder; t->alert_start_time = 0; t->start_yaw = 0; t->target_yaw_diff = 0; t->running_state = 0; // 明确重置运行状态为0 t->ccd_end_time = 0; t->is = 0; // 初始化坐标 t->target_x = 0.0f; t->target_y = 0.0f; t->current_x = 0.0f; t->current_y = 0.0f; t->rotation_start_left = 0; t->rotation_start_right = 0; t->target_rotation = 0.0f; } void next_state(TaskNamespace *t) { TaskState last_state = t->state; reset_task_namespace(t); if (last_state < T4) { t->state = last_state + 1; } } TaskNamespace task_namespace; void show_task_now(void) { //OLED_ShowString(0, 0, "Task Now:"); switch (task_namespace.state) { case BEGIN: OLED_ShowString(1, 10,"0"); break; case T1: OLED_ShowString(1, 10,"1"); break; case T2: OLED_ShowString(1, 10,"2"); break; case T3: OLED_ShowString(1, 10,"3"); break; case T4: OLED_ShowString(1, 10,"4"); break; default: break; } } void main_task(void); int main(void) { // 系统初始化 SYSCFG_DL_init(); // 初始化系统配置 hsu_time_init(); // 时间 // 清除所有外设的中断挂起状态 NVIC_ClearPendingIRQ(ENCODERA_INT_IRQN); // 编码器A中断 NVIC_ClearPendingIRQ(ENCODERB_INT_IRQN); // 编码器B中断 NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN); // UART0串口中断 // 使能各外设的中断 NVIC_EnableIRQ(ENCODERA_INT_IRQN); // 开启编码器A中断 NVIC_EnableIRQ(ENCODERB_INT_IRQN); // 开启编码器B中断 NVIC_EnableIRQ(UART_0_INST_INT_IRQN); // 开启UART0中断 reset_task_namespace(&task_namespace); task_namespace.state = BEGIN; // 明确设置初始状态 // 定时器和ADC相关中断配置 NVIC_ClearPendingIRQ(TIMER_0_INST_INT_IRQN); // 清除定时器0中断挂起 NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN); // 开启定时器0中断 NVIC_EnableIRQ(ADC12_VOLTAGE_INST_INT_IRQN); NVIC_EnableIRQ(ADC12_CCD_INST_INT_IRQN); OLED_Init(); // 初始化OLED显示屏 OLED_ShowString(1, 1, "Task Now:"); OLED_ShowString(2, 1, "state:"); OLED_ShowString(3, 1, "yaw:"); OLED_ShowString(4, 1, "x:"); OLED_ShowString(4, 6, "y:"); //MPU6050_initialize(); //DMP_Init(); BUZZY_ON(); // 主循环 // printf("Test delay 500us\n"); // hsu_time_delay_us(500); // printf("Test delay 500us end\n"); uint8_t main_task_timer = hsu_time_timer_create(10, true, main_task); hsu_time_timer_start(main_task_timer); uint8_t refresh_oled_timer = hsu_time_timer_create(5, true, refresh_oled); hsu_time_timer_start(refresh_oled_timer); uint8_t key_timer = hsu_time_timer_create(2, true, key); hsu_time_timer_start(key_timer); while (1) { hsu_time_timer_process(); RD_TSL(); // 读取CCD数据 Find_CCD_Median(); // 计算CCD数据中值 Read_DMP(); show_task_now(); //DL_GPIO_togglePins(LED_PORT, LED_led_PIN); // printf("L=%lld R=%lld YAW=%.1f\n", left_encoder, right_encoder, Yaw); } } void task_no(void); void task_1(void); void task_2(void); void task_3(void); void task_4(void); void main_task(void) { if (!(task_namespace.is)) return; printf("main task\n"); switch (task_namespace.state) { case BEGIN: task_no(); break; case T1: task_1(); break; case T2: task_2(); break; case T3: task_3(); break; case T4: task_4(); break; default: break; } switch (task_namespace.doing_what) { case STOP: Get_Target_Encoder(0, 0); break; case GO_STRAIGHT: if ((left_encoder * 1.f) < task_namespace.target) { Get_Target_Encoder(0.6, 0); // 提高速度到600mm/s } else { Get_Target_Encoder(0, 0); task_namespace.doing_what = STOP; task_namespace.finish = 1; } break; case GO_CCD: if (task_namespace.ccd_end_time < hsu_time_get_ms()) { Get_Target_Encoder(0, 0); task_namespace.doing_what = STOP; task_namespace.finish = 1; } else { CCD_Mode(); } break; case TURN_IN_PLACE: // 原地转向控制 if (task_namespace.target_yaw_diff != 0) { float current_yaw_diff = Yaw - task_namespace.start_yaw; // 处理角度跨越±180度的情况 if (current_yaw_diff > 180) { current_yaw_diff -= 360; } else if (current_yaw_diff < -180) { current_yaw_diff += 360; } printf("Turn: Start=%.1f Current=%.1f Diff=%.1f Target=%.1f\n", task_namespace.start_yaw, Yaw, current_yaw_diff, task_namespace.target_yaw_diff); // 检查是否达到目标角度 if ((task_namespace.target_yaw_diff > 0 && current_yaw_diff >= task_namespace.target_yaw_diff) || (task_namespace.target_yaw_diff < 0 && current_yaw_diff <= task_namespace.target_yaw_diff)) { Get_Target_Encoder(0, 0); // 停止转向 task_namespace.doing_what = STOP; task_namespace.finish = 1; } else { // 继续转向 float turn_speed = (task_namespace.target_yaw_diff > 0) ? 0.1 : -0.1; Get_Target_Encoder(0, turn_speed); } } break; case TURN_90_DEGREES: { // 计算当前旋转角度(使用编码器差值) float rotation = (left_encoder - task_namespace.rotation_start_left - (right_encoder - task_namespace.rotation_start_right)) * M_PI / (2 * WHEEL_BASE); // 检查是否达到目标角度(允许±0.1弧度误差) if (fabs(rotation - task_namespace.target_rotation) < 0.1f) { Get_Target_Encoder(0, 0); // 停止旋转 task_namespace.doing_what = STOP; task_namespace.finish = 1; } else { // 控制旋转速度(根据方向调整) float turn_speed = (task_namespace.target_rotation > 0) ? 0.1f : -0.1f; Get_Target_Encoder(-turn_speed, turn_speed); // 左右轮反向运动 } break; } case WAIT_ALERT: Get_Target_Encoder(0, 0); // 停车 if (hsu_time_get_ms() - task_namespace.alert_start_time > 1000) { // 声光提示1秒 task_namespace.doing_what = STOP; task_namespace.finish = 1; } break; default: break; } } void task_no(void) { return; } // 任务1:A点到B点直线行驶 void task_1(void) { if (!task_namespace.is_running) { task_namespace.is_running = 1; task_namespace.sub_task_stage = 0; task_namespace.finish = 1; return; } if (task_namespace.finish) { switch (task_namespace.sub_task_stage) { case 0: // 开始第一阶段:A到B go_straight(3300); break; case 1: // 完成亮灯 DL_GPIO_togglePins(LED_PORT, LED_led_PIN); break; case 2: //任务结束 reset_task_namespace(&task_namespace); task_namespace.running_state = 0; // 重置为停止状态 break; } task_namespace.finish = 0; task_namespace.sub_task_stage++; } return; } // 任务2:X Y坐标行驶 void task_2(void) { if (!task_namespace.is_running) { task_namespace.is_running = 1; task_namespace.sub_task_stage = 0; task_namespace.finish = 1; return; } if (task_namespace.finish) { switch (task_namespace.sub_task_stage) { case 0: // 运动到XY坐标 go_to_xy(3000.0f, 3000.0f); break; case 1: // 完成灯亮 DL_GPIO_togglePins(LED_PORT, LED_led_PIN); break; case 2: // 任务结束 sound_light_alert(); reset_task_namespace(&task_namespace); break; } task_namespace.finish = 0; task_namespace.sub_task_stage++; } return; } // 任务3:A->C->B->D->A循环 void task_3(void) { if (!task_namespace.is_running) { task_namespace.is_running = 1; task_namespace.sub_task_stage = 0; task_namespace.finish = 1; return; } if (task_namespace.finish) { switch (task_namespace.sub_task_stage) { case 0: turn_in_place(-31.0f); break; case 1: go_straight(4060); break; case 2: turn_in_place(30.0f); break; case 3: // 开始C到B弧线 sound_light_alert(); //go_straight(40); go_brc_ccd(3550); break; case 4: turn_in_place(36.0f); break; case 5: sound_light_alert(); go_straight(3985); break; case 6: turn_in_place(-40.0f); break; case 7: // 开始C到B弧线 sound_light_alert(); go_arc_ccd(3600); break; case 8: // D到A弧线完成,任务结束 sound_light_alert(); reset_task_namespace(&task_namespace); break; } task_namespace.finish = 0; task_namespace.sub_task_stage++; } return; } // 任务4:重复任务3路径4圈 void task_4(void) { if (!task_namespace.is_running) { task_namespace.is_running = 1; task_namespace.sub_task_stage = 0; task_namespace.finish = 1; return; } if (task_namespace.finish) { switch (task_namespace.sub_task_stage) { case 0: turn_in_place(-30.0f); break; case 1: go_straight(4045); break; case 2: turn_in_place(29.0f); break; case 3: // 开始C到B弧线 sound_light_alert(); //go_straight(40); go_brc_ccd(5000); break; case 4: turn_in_place(34.0f); break; case 5: sound_light_alert(); go_straight(4035); break; case 6: turn_in_place(-33.0f); break; case 7: // 开始C到B弧线 sound_light_alert(); go_arc_ccd(5000); break; case 8: // D到A弧线完成,任务结束 sound_light_alert(); reset_task_namespace(&task_namespace); break; } task_namespace.finish = 0; task_namespace.sub_task_stage++; } } void TIMER_0_INST_IRQHandler(void) { if (DL_TimerA_getPendingInterrupt(TIMER_0_INST)) { if (DL_TIMER_IIDX_ZERO) { Get_Velocity_From_Encoder(Get_Encoder_countA, Get_Encoder_countB); Get_Encoder_countA = Get_Encoder_countB = 0; MotorA.Motor_Pwm = Incremental_PI_Left(MotorA.Current_Encoder, MotorA.Target_Encoder); MotorB.Motor_Pwm = Incremental_PI_Right(MotorB.Current_Encoder, MotorB.Target_Encoder); if (!Flag_Stop) { Set_PWM(-MotorA.Motor_Pwm, -MotorB.Motor_Pwm); } else { Set_PWM(0, 0); } } } } uint32_t gpio_interrup1, gpio_interrup2; int64_t B1, B2, B3, B4; int64_t A1, A2, A3, A4; void GROUP1_IRQHandler(void) { // 获取中断信号 gpio_interrup1 = DL_GPIO_getEnabledInterruptStatus(ENCODERA_PORT, ENCODERA_E1A_PIN | ENCODERA_E1B_PIN); gpio_interrup2 = DL_GPIO_getEnabledInterruptStatus(ENCODERB_PORT, ENCODERB_E2A_PIN | ENCODERB_E2B_PIN); // encoderB if ((gpio_interrup1 & ENCODERA_E1A_PIN) == ENCODERA_E1A_PIN) { if (!DL_GPIO_readPins(ENCODERA_PORT, ENCODERA_E1B_PIN)) { right_encoder--; Get_Encoder_countB--; } else { right_encoder++; Get_Encoder_countB++; } } else if ((gpio_interrup1 & ENCODERA_E1B_PIN) == ENCODERA_E1B_PIN) { if (!DL_GPIO_readPins(ENCODERA_PORT, ENCODERA_E1A_PIN)) { right_encoder++; Get_Encoder_countB++; } else { right_encoder--; Get_Encoder_countB--; } } // encoderA if ((gpio_interrup2 & ENCODERB_E2A_PIN) == ENCODERB_E2A_PIN) { if (!DL_GPIO_readPins(ENCODERB_PORT, ENCODERB_E2B_PIN)) { left_encoder++; Get_Encoder_countA--; } else { left_encoder--; Get_Encoder_countA++; } } else if ((gpio_interrup2 & ENCODERB_E2B_PIN) == ENCODERB_E2B_PIN) { if (!DL_GPIO_readPins(ENCODERB_PORT, ENCODERB_E2A_PIN)) { left_encoder--; Get_Encoder_countA++; } else { left_encoder++; Get_Encoder_countA--; } } DL_GPIO_clearInterruptStatus(ENCODERA_PORT, ENCODERA_E1A_PIN | ENCODERA_E1B_PIN); DL_GPIO_clearInterruptStatus(ENCODERB_PORT, ENCODERB_E2A_PIN | ENCODERB_E2B_PIN); } // 直线行驶函数 void go_straight(int dis) { task_namespace.doing_what = GO_STRAIGHT; task_namespace.target = left_encoder + dis; task_namespace.finish = 0; } // 原地转向函数 void turn_in_place(float angle) { task_namespace.doing_what = TURN_IN_PLACE; task_namespace.start_yaw = Yaw; task_namespace.target_yaw_diff = angle; // 正值右转,负值左转 task_namespace.finish = 0; } // CCD巡线函数(需要外部条件结束) void go_ccd_line(void) { task_namespace.doing_what = GO_CCD; task_namespace.start_encoder = left_encoder; task_namespace.finish = 0; // 设置一个安全的最大距离,防止无限巡线 // 可以根据实际场地调整这个值 static uint32_t ccd_end_time = 0; if (ccd_end_time == 0) { ccd_end_time = hsu_time_get_ms(); } // 如果巡线时间超过10秒或距离超过2000mm,强制结束 if (hsu_time_get_ms() - ccd_end_time > 10000 || (left_encoder * 1.f - task_namespace.start_encoder) > 2000) { task_namespace.finish = 1; ccd_end_time = 0; } } // 弧线CCD巡线函数 void go_arc_ccd(hsu_time_t time) { task_namespace.doing_what = GO_CCD; task_namespace.start_encoder = left_encoder; task_namespace.ccd_end_time = hsu_time_get_ms() + time; } void go_brc_ccd(hsu_time_t time) { task_namespace.doing_what = GO_CCD; task_namespace.start_encoder = right_encoder; task_namespace.ccd_end_time = hsu_time_get_ms() + time; } // 旋转90度函数 /*void turn_90_degrees(int direction) { // direction: 1为顺时针,-1为逆时针 turn_in_place(90.0f * direction); }*/ // 运动到指定坐标函数 void go_to_xy(float target_x, float target_y) { task_namespace.sub_task_stage = 0; // 重置子任务阶段 // 第一阶段:运动到X坐标 float x_distance = target_x - task_namespace.current_x; go_straight((int)fabs(x_distance)); // 使用绝对值距离 // 更新当前坐标 task_namespace.current_x = target_x; // 第二阶段:旋转90度(方向根据Y坐标位置决定) int direction = (target_y > task_namespace.current_y) ? 1 : -1; turn_90_degrees(direction); // 第三阶段:运动到Y坐标 float y_distance = target_y - task_namespace.current_y; go_straight((int)fabs(y_distance)); // 使用绝对值距离 // 更新当前坐标 task_namespace.current_y = target_y; } // 旋转90度函数(使用编码器计算) void turn_90_degrees(int direction) { // 保存旋转开始时的编码器值 task_namespace.rotation_start_left = left_encoder; task_namespace.rotation_start_right = right_encoder; // 计算目标旋转量(90度 = π/2 弧度) // 假设轮距为120mm(根据实际小车尺寸调整) #define WHEEL_BASE 80.0f task_namespace.target_rotation = (direction > 0) ? (M_PI/2) : (-M_PI/2); // 设置状态为旋转 task_namespace.doing_what = TURN_90_DEGREES; task_namespace.finish = 0; } // 声光提示函数 void sound_light_alert(void) { DL_GPIO_togglePins(LED_PORT, LED_led_PIN); DL_GPIO_setPins(BUZZY_PORT, BUZZY_PIN_PIN); uint32_t start_time = hsu_time_get_ms(); while (hsu_time_get_ms() - start_time < 1000) { // 空循环等待1秒 } //hsu_time_delay_ms(200); DL_GPIO_togglePins(LED_PORT, LED_led_PIN); DL_GPIO_clearPins(BUZZY_PORT, BUZZY_PIN_PIN); } // callback void refresh_oled(void) { show_task_now(); OLED_ShowString(2, 1, "state:"); if (task_namespace.running_state) { OLED_ShowString(2, 7, "1"); // 运行中 } else { OLED_ShowString(2, 7, "0"); // 停止 } } uint32_t key_get_tick_ms(void) { return hsu_time_get_ms(); } void key(void) { key_event_t event = key_scan(); //uint8_t key_value = key_read_pin(); // 获取按键状态 //S1 switch (event) { case KEY_EVENT_SINGLE_CLICK: next_state(&task_namespace); break; case KEY_EVENT_DOUBLE_CLICK: task_namespace.is = 1; task_namespace.running_state = 1; task_namespace.is_running = 0; // 重置任务运行标志 break; } }
最新发布
07-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值