从没有中断引脚并且在测量准备好之前需要一些时间的传感器读取数据的最佳方法

2024-01-31

我正在尝试将压力传感器 (MS5803-14BA) 与我的板 (NUCLEO-STM32L073RZ) 连接。

根据(第 3 页),压力传感器需要几毫秒才能准备好读取测量值。对于我的项目,我对需要大约 10 毫秒来转换原始数据的最高分辨率感兴趣。

不幸的是,这个压力传感器没有任何中断引脚可以用来查看测量何时准备好,因此我暂时解决了在请求新数据后延迟的问题。

我不喜欢我当前的解决方案,因为在这 10 毫秒内,我可以让 MCU 处理其他事情(我的板上连接了其他几个传感器),但没有任何中断引脚,我不确定是什么解决这个问题的最好方法。

我想到了另一个解决方案:使用一个计时器,每隔 20 毫秒触发一次并执行以下操作:

1.a Read the current value stored in the registers (discarding the first value)
1.b Ask for a new value

这样,在下一次迭代时,我只需要读取上一次迭代结束时请求的值。

我不喜欢的是我的测量结果总是 20 毫秒。在延迟保持 20 毫秒之前,应该还可以,但如果我需要降低速率,我的解决方案的读数“年龄”就会增加。

您对于如何处理这个问题还有其他想法吗?

谢谢。

注意:如果您需要查看我当前的实现,请告诉我。


如何做到高分辨率、基于时间戳、非阻塞、单线程协作多任务

这不是“如何读取传感器”问题,而是“如何进行非阻塞协作多任务处理”问题。假设你正在跑步裸机(没有操作系统,例如FreeRTOS),你有两个不错的选择。

First, the datasheet shows you need to wait up to 9.04 ms, or 9040 us. enter image description here

现在,这是您的协作多任务选项:

  1. 发送命令告诉设备进行 ADC 转换(即:进行模拟测量),然后配置硬件定时器在 9040 us 后准确中断您。然后,您可以在 ISR 中设置一个标志来告诉主循环发送读取命令来读取结果,或者您可以直接在 ISR 内部发送读取命令。

  2. 在主循环中使用基于非阻塞时间戳的协作多任务处理。这可能需要一个基本的状态机。发送转换命令,然后继续,做其他事情。当您的时间戳表明它已经足够长时,发送读取命令以从传感器读取转换后的结果。

上面的第 1 种方法是我处理时间紧迫的任务时首选的方法。然而,这对时间要求不高,一点点抖动不会产生任何影响,因此上面的第 2 种方法是我在一般裸机协作多任务处理中的首选方法,所以让我们这样做。

以下是一个示例程序,用于演示基于时间戳的裸机协作多任务处理的原理,适合您需要的特定情况:

  1. 请求数据样本(在外部传感器中启动 ADC 转换)
  2. 等待 9040 us 才能完成转换
  3. 从外部传感器读取数据样本(现在 ADC 转换已完成)

Code:

enum sensorState_t 
{
    SENSOR_START_CONVERSION,
    SENSOR_WAIT,
    SENSOR_GET_CONVERSION
}

int main(void)
{
    doSetupStuff();
    configureHardwareTimer(); // required for getMicros() to work

    while (1)
    {
        //
        // COOPERATIVE TASK #1
        // Read the under-water pressure sensor as fast as permitted by the datasheet
        //
        static sensorState_t sensorState = SENSOR_START_CONVERSION; // initialize state machine
        static uint32_t task1_tStart; // us; start time
        static uint32_t sensorVal; // the sensor value you are trying to obtain 
        static bool newSensorVal = false; // set to true whenever a new value arrives
        switch (sensorState)
        {
            case SENSOR_START_CONVERSION:
            {
                startConversion(); // send command to sensor to start ADC conversion
                task1_tStart = getMicros(); // get a microsecond time stamp
                sensorState = SENSOR_WAIT; // next state 
                break;
            }
            case SENSOR_WAIT:
            {
                const uint32_t DESIRED_WAIT_TIME = 9040; // us
                uint32_t tNow = getMicros();
                if (tNow - task1_tStart >= DESIRED_WAIT_TIME)
                {
                    sensorState = SENSOR_GET_CONVERSION; // next state
                }
                break;
            }
            case SENSOR_GET_CONVERSION:
            {
                sensorVal = readConvertedResult(); // send command to read value from the sensor
                newSensorVal = true;
                sensorState = SENSOR_START_CONVERSION; // next state 
                break;
            }
        }

        //
        // COOPERATIVE TASK #2
        // use the under-water pressure sensor data right when it comes in (this will be an event-based task
        // whose running frequency depends on the rate of new data coming in, for example)
        //
        if (newSensorVal == true)
        {
            newSensorVal = false; // reset this flag 

            // use the sensorVal data here now for whatever you need it for
        }


        //
        // COOPERATIVE TASK #3
        //


        //
        // COOPERATIVE TASK #4
        //


        // etc etc

    } // end of while (1)
} // end of main

对于另一个非常简单的基于时间戳的多任务示例,请参见Arduino“无延迟闪烁”示例在这里 https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay.

General 基于时间戳的裸机协作多任务处理架构笔记:

根据您的操作方式,最终您基本上会得到这种类型的代码布局,它只是以固定的时间间隔运行每个任务。每个任务应该是非阻塞以确保它不会与其他任务的运行间隔冲突。裸机上的非阻塞意味着“不要使用浪费时钟的延迟、繁忙循环或其他类型的轮询、重复、计数或繁忙延迟!”。 (这与基于操作系统(OS-based)的系统上的“阻塞”相反,这意味着“将时钟返还给调度程序,让它在该任务‘休眠’时运行另一个线程。”记住:裸机 means 无操作系统!)。相反,如果某些东西还没有完全准备好运行,只需通过状态机保存您的状态,退出该任务的代码(这是“合作”部分,因为您的任务必须通过返回来自愿放弃处理器),然后让另一个任务运行!

这是基本架构,展示了一种简单的基于时间戳的方法,可以让 3 个任务以独立的固定频率运行,而不依赖于任何中断,并且最小抖动,由于我采用彻底且有条理的方法来检查时间戳并在每次运行时更新开始时间。

1、定义为main()函数和主循环:

int main(void)
{
    doSetupStuff();
    configureHardwareTimer();

    while (1)
    {
        doTask1();
        doTask2();
        doTask3();
    }
}

2、 的定义doTask()功能:

// Task 1: Let's run this one at 100 Hz (every 10ms)
void doTask1(void)
{
    const uint32_t DT_DESIRED_US = 10000; // 10000us = 10ms, or 100Hz run freq
    static uint32_t t_start_us = getMicros();
    uint32_t t_now_us = getMicros();
    uint32_t dt_us = t_now_us - t_start_us;

    // See if it's time to run this Task
    if (dt_us >= DT_DESIRED_US)
    {
        // 1. Add DT_DESIRED_US to t_start_us rather than setting t_start_us to t_now_us (which many 
        // people do) in order to ***avoid introducing artificial jitter into the timing!***
        t_start_us += DT_DESIRED_US;
        // 2. Handle edge case where it's already time to run again because just completing one of the main
        // "scheduler" loops in the main() function takes longer than DT_DESIRED_US; in other words, here
        // we are seeing that t_start_us is lagging too far behind (more than one DT_DESIRED_US time width
        // from t_now_us), so we are "fast-forwarding" t_start_us up to the point where it is exactly 
        // 1 DT_DESIRED_US time width back now, thereby causing this task to instantly run again the 
        // next time it is called (trying as hard as we can to run at the specified frequency) while 
        // at the same time protecting t_start_us from lagging farther and farther behind, as that would
        // eventually cause buggy and incorrect behavior when the (unsigned) timestamps start to roll over
        // back to zero.
        dt_us = t_now_us - t_start_us; // calculate new time delta with newly-updated t_start_us
        if (dt_us >= DT_DESIRED_US)
        {
            t_start_us = t_now_us - DT_DESIRED_US;
        }

        // PERFORM THIS TASK'S OPERATIONS HERE!

    }
}

// Task 2: Let's run this one at 1000 Hz (every 1ms)
void doTask2(void)
{
    const uint32_t DT_DESIRED_US = 1000; // 1000us = 1ms, or 1000Hz run freq
    static uint32_t t_start_us = getMicros();
    uint32_t t_now_us = getMicros();
    uint32_t dt_us = t_now_us - t_start_us;

    // See if it's time to run this Task
    if (dt_us >= DT_DESIRED_US)
    {
        t_start_us += DT_DESIRED_US;
        dt_us = t_now_us - t_start_us; // calculate new time delta with newly-updated t_start_us
        if (dt_us >= DT_DESIRED_US)
        {
            t_start_us = t_now_us - DT_DESIRED_US;
        }

        // PERFORM THIS TASK'S OPERATIONS HERE!

    }
}

// Task 3: Let's run this one at 10 Hz (every 100ms)
void doTask3(void)
{
    const uint32_t DT_DESIRED_US = 100000; // 100000us = 100ms, or 10Hz run freq
    static uint32_t t_start_us = getMicros();
    uint32_t t_now_us = getMicros();
    uint32_t dt_us = t_now_us - t_start_us;

    // See if it's time to run this Task
    if (dt_us >= DT_DESIRED_US)
    {
        t_start_us += DT_DESIRED_US;
        dt_us = t_now_us - t_start_us; // calculate new time delta with newly-updated t_start_us
        if (dt_us >= DT_DESIRED_US)
        {
            t_start_us = t_now_us - DT_DESIRED_US;
        }

        // PERFORM THIS TASK'S OPERATIONS HERE!

    }
}

上面的代码工作得很好,但正如你所看到的,它相当多余,并且设置新任务有点烦人。通过简单地定义一个宏,这项工作可以更加自动化,并且更容易完成,CREATE_TASK_TIMER()如下所示,为我们完成所有冗余计时工作和时间戳变量创建:

/// @brief      A function-like macro to get a certain set of events to run at a desired, fixed 
///             interval period or frequency.
/// @details    This is a timestamp-based time polling technique frequently used in bare-metal
///             programming as a basic means of achieving cooperative multi-tasking. Note 
///             that getting the timing details right is difficult, hence one reason this macro 
///             is so useful. The other reason is that this maro significantly reduces the number of
///             lines of code you need to write to introduce a new timestamp-based cooperative
///             task. The technique used herein achieves a perfect desired period (or freq) 
///             on average, as it centers the jitter inherent in any polling technique around 
///             the desired time delta set-point, rather than always lagging as many other 
///             approaches do.
///             
///             USAGE EX:
///             ```
///             // Create a task timer to run at 500 Hz (every 2000 us, or 2 ms; 1/0.002 sec = 500 Hz)
///             const uint32_t PERIOD_US = 2000; // 2000 us pd --> 500 Hz freq
///             bool time_to_run;
///             CREATE_TASK_TIMER(PERIOD_US, time_to_run);
///             if (time_to_run)
///             {
///                 run_task_2();
///             }
///             ```
///
///             Source: Gabriel Staples 
///             https://stackoverflow.com/questions/50028821/best-way-to-read-from-a-sensors-that-doesnt-have-interrupt-pin-and-require-some/50032992#50032992
/// @param[in]  dt_desired_us   The desired delta time period, in microseconds; note: pd = 1/freq;
///                             the type must be `uint32_t`
/// @param[out] time_to_run     A `bool` whose scope will enter *into* the brace-based scope block
///                             below; used as an *output* flag to the caller: this variable will 
///                             be set to true if it is time to run your code, according to the 
///                             timestamps, and will be set to false otherwise
/// @return     NA--this is not a true function
#define CREATE_TASK_TIMER(dt_desired_us, time_to_run)                                                                  \
{ /* Use scoping braces to allow multiple calls of this macro all in one outer scope while */                          \
  /* allowing each variable created below to be treated as unique to its own scope */                                  \
    time_to_run = false;                                                                                               \
                                                                                                                       \
    /* set the desired run pd / freq */                                                                                \
    const uint32_t DT_DESIRED_US = dt_desired_us;                                                                      \
    static uint32_t t_start_us = getMicros();                                                                          \
    uint32_t t_now_us = getMicros();                                                                                   \
    uint32_t dt_us = t_now_us - t_start_us;                                                                            \
                                                                                                                       \
    /* See if it's time to run this Task */                                                                            \
    if (dt_us >= DT_DESIRED_US)                                                                                        \
    {                                                                                                                  \
        /* 1. Add DT_DESIRED_US to t_start_us rather than setting t_start_us to t_now_us (which many */                \
        /* people do) in order to ***avoid introducing artificial jitter into the timing!*** */                        \
        t_start_us += DT_DESIRED_US;                                                                                   \
        /* 2. Handle edge case where it's already time to run again because just completing one of the main */         \
        /* "scheduler" loops in the main() function takes longer than DT_DESIRED_US; in other words, here */           \
        /* we are seeing that t_start_us is lagging too far behind (more than one DT_DESIRED_US time width */          \
        /* from t_now_us), so we are "fast-forwarding" t_start_us up to the point where it is exactly */               \
        /* 1 DT_DESIRED_US time width back now, thereby causing this task to instantly run again the */                \
        /* next time it is called (trying as hard as we can to run at the specified frequency) while */                \
        /* at the same time protecting t_start_us from lagging farther and farther behind, as that would */            \
        /* eventually cause buggy and incorrect behavior when the (unsigned) timestamps start to roll over */          \
        /* back to zero. */                                                                                            \
        dt_us = t_now_us - t_start_us; /* calculate new time delta with newly-updated t_start_us */                    \
        if (dt_us >= DT_DESIRED_US)                                                                                    \
        {                                                                                                              \
            t_start_us = t_now_us - DT_DESIRED_US;                                                                     \
        }                                                                                                              \
                                                                                                                       \
        time_to_run = true;                                                                                            \
    }                                                                                                                  \
}

现在,有多种方法可以使用它,但是为了这个演示,为了保持真正的干净main()循环代码如下所示:

int main(void)
{
    doSetupStuff();
    configureHardwareTimer();

    while (1)
    {
        doTask1();
        doTask2();
        doTask3();
    }
}

让我们使用CREATE_TASK_TIMER()像这样的宏。正如您所看到的,代码现在更加简洁,并且更容易设置新任务。这是我的首选方法,因为它创建了上面所示的真正干净的主循环,仅包含各种doTask()调用,也很容易编写和维护:

// Task 1: Let's run this one at 100 Hz (every 10ms, or 10000us)
void doTask1(void)
{
    bool time_to_run;
    const uint32_t DT_DESIRED_US = 10000; // 10000us = 10ms, or 100Hz run freq
    CREATE_TASK_TIMER(DT_DESIRED_US, time_to_run);
    if (time_to_run)
    {
        // PERFORM THIS TASK'S OPERATIONS HERE!
    }
}

// Task 2: Let's run this one at 1000 Hz (every 1ms)
void doTask2(void)
{
    bool time_to_run;
    const uint32_t DT_DESIRED_US = 1000; // 1000us = 1ms, or 1000Hz run freq
    CREATE_TASK_TIMER(DT_DESIRED_US, time_to_run);
    if (time_to_run)
    {
        // PERFORM THIS TASK'S OPERATIONS HERE!
    }
}

// Task 3: Let's run this one at 10 Hz (every 100ms)
void doTask3(void)
{
    bool time_to_run;
    const uint32_t DT_DESIRED_US = 100000; // 100000us = 100ms, or 10Hz run freq
    CREATE_TASK_TIMER(DT_DESIRED_US, time_to_run);
    if (time_to_run)
    {
        // PERFORM THIS TASK'S OPERATIONS HERE!
    }
}

但是,您也可以像这样构造代码,它的工作原理相同并产生相同的效果,只是方式略有不同:

#include <stdbool.h>
#include <stdint.h>

#define TASK1_PD_US (10000)     // 10ms pd, or 100 Hz run freq 
#define TASK2_PD_US (1000)      // 1ms pd, or 1000 Hz run freq 
#define TASK3_PD_US (100000)    // 100ms pd, or 10 Hz run freq 

// Task 1: Let's run this one at 100 Hz (every 10ms, or 10000us)
void doTask1(void)
{
    // PERFORM THIS TASK'S OPERATIONS HERE!
}

// Task 2: Let's run this one at 1000 Hz (every 1ms)
void doTask2(void)
{
    // PERFORM THIS TASK'S OPERATIONS HERE!
}

// Task 3: Let's run this one at 10 Hz (every 100ms)
void doTask3(void)
{
    // PERFORM THIS TASK'S OPERATIONS HERE!
}

int main(void)
{
    doSetupStuff();
    configureHardwareTimer();

    while (1)
    {
        bool time_to_run;

        CREATE_TASK_TIMER(TASK1_PD_US, time_to_run);
        if (time_to_run)
        {
            doTask1();
        }

        CREATE_TASK_TIMER(TASK2_PD_US, time_to_run);
        if (time_to_run)
        {
            doTask2();
        }

        CREATE_TASK_TIMER(TASK3_PD_US, time_to_run);
        if (time_to_run)
        {
            doTask3();
        }
    }
}

嵌入式裸机微控制器编程的艺术(也是乐趣!)的一部分在于准确决定如何交错每个任务并使它们一起运行(就像它们并行运行一样)所涉及的技能和独创性。使用上述格式之一作为起点,并适应您的特定情况。可以根据需要以及特定应用程序的需要在任务之间或任务与中断、任务与用户等之间添加消息传递。

下面是如何配置定时器以用作 STM32F2 微控制器上的时间戳生成器的示例。

这显示了以下功能:configureHardwareTimer() and getMicros(),上面使用:

// Timer handle to be used for Timer 2 below
TIM_HandleTypeDef TimHandle;

// Configure Timer 2 to be used as a free-running 32-bit hardware timer for general-purpose use as a 1-us-resolution
// timestamp source
void configureHardwareTimer()
{
    // Timer clock must be enabled before you can configure it
    __HAL_RCC_TIM2_CLK_ENABLE();

    // Calculate prescaler
    // Here are some references to show how this is done:
    // 1) "STM32Cube_FW_F2_V1.7.0/Projects/STM32F207ZG-Nucleo/Examples/TIM/TIM_OnePulse/Src/main.c" shows the
    //    following (slightly modified) equation on line 95: `Prescaler = (TIMxCLK/TIMx_counter_clock) - 1`
    // 2) "STM32F20x and STM32F21x Reference Manual" states the following on pg 419: "14.4.11 TIMx prescaler (TIMx_PSC)"
    //    "The counter clock frequency CK_CNT is equal to fCK_PSC / (PSC[15:0] + 1)"
    //    This means that TIMx_counter_clock_freq = TIMxCLK/(prescaler + 1). Now, solve for prescaler and you
    //    get the exact same equation as above: `prescaler = TIMxCLK/TIMx_counter_clock_freq - 1`
    // Calculating TIMxCLK:
    // - We must divide SystemCoreClock (returned by HAL_RCC_GetHCLKFreq()) by 2 because TIM2 uses clock APB1
    // as its clock source, and on my board this is configured to be 1/2 of the SystemCoreClock.
    // - Note: To know which clock source each peripheral and timer uses, you can look at
    //  "Table 25. Peripheral current consumption" in the datasheet, p86-88.
    const uint32_t DESIRED_TIMER_FREQ = 1e6; // 1 MHz clock freq --> 1 us pd per tick, which is what I want
    uint32_t Tim2Clk = HAL_RCC_GetHCLKFreq() / 2;
    uint32_t prescaler = Tim2Clk / DESIRED_TIMER_FREQ - 1; // Don't forget the minus 1!

    // Configure timer
    // TIM2 is a 32-bit timer; See datasheet "Table 4. Timer feature comparison", p30-31
    TimHandle.Instance               = TIM2;
    TimHandle.Init.Period            = 0xFFFFFFFF; // Set pd to max possible for a 32-bit timer
    TimHandle.Init.Prescaler         = prescaler;
    TimHandle.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
    TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;
    TimHandle.Init.RepetitionCounter = 0; // NA (has no significance) for this timer

    // Initialize the timer
    if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
    {
        // handle error condition
    }

    // Start the timer
    if (HAL_TIM_Base_Start(&TimHandle) != HAL_OK)
    {
        // handle error condition
    }
}

// Get the 1 us count value on Timer 2.
// This timer will be used for general purpose hardware timing that does NOT rely on interrupts.
// Therefore, the counter will continue to increment even with interrupts disabled.
// The count value increments every 1 microsecond.
// Since it is a 32-bit counter it overflows every 2^32 counts, which means the highest value it can
// store is 2^32 - 1 = 4294967295. Overflows occur every 2^32 counts / 1 count/us / 1e6us/sec
// = ~4294.97 sec = ~71.6 min.
uint32_t getMicros()
{
    return __HAL_TIM_GET_COUNTER(&TimHandle);
}

参考:

  1. https://www.arduino.cc/en/tutorial/BlinkWithoutDelay https://www.arduino.cc/en/tutorial/BlinkWithoutDelay
  2. 多氧:在 Doxygen 中引用参数的正确方法是什么? https://stackoverflow.com/questions/15398711/whats-the-right-way-to-reference-a-parameter-in-doxygen/56745246#56745246
  3. 用于错误处理的基于枚举的错误代码:C 代码中的错误处理 https://stackoverflow.com/questions/385975/error-handling-in-c-code/59221452#59221452
  4. C 中的其他架构风格,例如通过不透明指针的“基于对象”C:不透明 C 结构:声明它们的各种方法 https://stackoverflow.com/questions/3965279/opaque-c-structs-how-should-they-be-declared/54488289#54488289

也可以看看:

  1. A full, runnable Arduino example with an even better version of my CREATE_TASK_TIMER() macro from above:
    1. 我对 C 和 C++ 的回答,包括微控制器和 Arduino(或任何其他系统):Full 库仑计数器使用基于时间戳、单线程、协作多任务处理演示上述概念的示例 https://arduino.stackexchange.com/a/75937/7727
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从没有中断引脚并且在测量准备好之前需要一些时间的传感器读取数据的最佳方法 的相关文章

  • 如何在 wpf 密码框设置一些默认文本? [复制]

    这个问题在这里已经有答案了 可能的重复 WPF 中的水印文本框 https stackoverflow com questions 833943 watermark textbox in wpf 我可以知道如何在 WPF 的密码框中放入一些
  • C# 中的简单获取字符串(忽略末尾的数字)

    我认为正则表达式太过杀伤力 而且它需要我一些时间来编写一些代码 我想我现在应该学习 因为我知道一些正则表达式 分隔字母数字字符串中的字符串的最简单方法是什么 它将永远是 LLLLDDDDD 我只想要字母 l 通常只有 1 或 2 个字母 T
  • 在opencv中保存帧而不压缩

    我正在尝试使用写 OpenCV 函数 我想保存帧 TIFF扩大 我遇到的问题是保存的图像被压缩 所以我无法使用它们 知道如何摆脱这种压缩吗 提前致谢 不要介意西奇说的话 TIFF 标志通过 LZW 压缩硬编码在 opencv 二进制文件中
  • 在运行时配置 ASP.NET 会话状态

    我们有一个使用 SQL Server 会话状态的 ASP NET 网站 状态配置在Web config like
  • 是否有一种快速替代方法可以从 XNA 中的位图对象创建 Texture2D?

    我环顾四周 发现从位图创建Texture2D的唯一方法是 using MemoryStream s new MemoryStream bmp Save s System Drawing Imaging ImageFormat Png s S
  • Windows Server / Datacenter:设置 CPU 关联性 > 64 个核心

    SetThreadAffinityMask 允许为 64 个逻辑核心 处理器 设置关联掩码 但是 Windows Datacenter 最多可以有 64 个 CPU 每个 CPU 都有多个内核 请参阅here http social tec
  • 从对象中获取类型正在返回运行时类型[重复]

    这个问题在这里已经有答案了 我有一个简单的功能 public string getType object obj Type type obj getType return type FullName 如果您在运行时创建的字符串对象上使用此函
  • 为什么 C++11/Boost `unordered_map` 在擦除时不重新散列?

    我想知道为什么 C 11 和 Boost 的 hashmap 在通过迭代擦除元素时不会调整大小 即使这在技术上不是内存泄漏 我认为这可能是应用程序中的一个严重问题 这对我来说是一个隐藏的问题 很难追踪它 并且它实际上可能会影响许多应用程序
  • 将 CryptoStream 解密为 MemoryStream

    我编写了一个过程 其中文件被加密并上传到 Azure 然后必须解密下载过程 这会失败并出现 填充无效且无法删除 错误 或 要解密的数据长度为无效的 错误 我在网上尝试了很多解决方案 包括C 使用 RijndaelManaged 和 Cryp
  • 如何设置属性选择器的值 Expression>

    我需要使用模式工厂的想法将 Person 类实体中的实体属性 Address 与 FactoryEntities 类中的表达式 linq 相关联 看看这就是我所拥有的并且我想要做的 Address address new Address a
  • 如何获取 TFS 2013 中所有用户的列表

    我正在使用 Team Foundation Server TFS 2013 和 Visual studio 2012 我需要 TFS 中所有用户的列表 有没有办法使用C 获取TFS中的所有用户 从TFS 2010获取用户列表 您可以尝试使用
  • 验证仅适用于数组的第一项

    给定这个模型代码 Required Display Name Name public string Name get set 以下查看代码有效 Html LabelFor model gt model Name Html TextBoxFo
  • 二叉树实现C++

    二叉树插入 include stdafx h include
  • 更改子进程中的 iostream

    现在 我正在开发一个项目 其中我需要启动一个子进程来使用 C 在 Linux 中执行一个新程序 并且我需要重定向标准输入和输出 就像在 C 中一样 它们是cin and cout 到一个文件 这意味着在子进程中 标准输入和输出都是文件 子进
  • LINQ 中的左外连接

    下面的代码不断给我一个错误消息 你调用的对象是空的 var partsWithDefaults from partsList1 in p join partsList2 in d on new PartNo partsList1 PartN
  • 使用全局 Web API 过滤器属性进行 Unity 依赖注入

    参考这个CodePlex 统一文章 http unity codeplex com discussions 446780我能够使用 WebAPI 控制器获取过滤器属性 如下所示 MyFilterAttribute public class
  • 如何将 typedef 结构传递给函数?

    此刻我正在努力 void avg everything 但这给了我错误 error subscripted value is neither array nor pointer 当我今天早些时候收到此错误时 这是 因为我没有正确地将 2D
  • C# - 平移光标

    我正在 PictureBox 控件中实现大图像的平移 并且设置适当的方向平移光标没有问题 但是 我似乎找不到用于平底锅原点的图像 内部带有箭头的圆圈 我在哪里可以找到它 我觉得image您正在寻找的内容未包含在框架中 每个应用程序都使用自己
  • 我如何将 C++ 与 VALA 混合起来

    我需要用 C 编写跨平台的 GUI 应用程序 但由于 C 的大多数 GUI 库都有点乏味 而且我对 C NET 非常熟悉 我发现使用 GTK 的代码 Vala 代码非常有趣 并且与其他方式相比有点容易 那么我该如何将 VAlA 与 C 混合
  • XslCompiledTransform 和自定义 XmlUrlResolver:“具有相同键的条目已存在”

    有没有办法调试由自定义 XmlUrlResolver 从数据库加载的 XSLT 文档 或者有人知道下面的错误消息是关于什么的吗 我有一个导入通用 xslt 文档的 XSLT 样式表

随机推荐