Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d70df219f8 | ||
|
|
7e854e10f0 | ||
|
|
b6c385e09c | ||
|
|
a9f76ea058 | ||
|
|
c40c714028 | ||
|
|
f87ab0c65f | ||
|
|
b4230b2a0e | ||
|
|
04e1ed0f16 |
@@ -4,6 +4,9 @@ set(priv_req driver)
|
||||
if(IDF_TARGET STREQUAL "esp32")
|
||||
list(APPEND srcs wroom/hal_audio.c)
|
||||
list(APPEND priv_req board_wroom)
|
||||
elseif(IDF_TARGET STREQUAL "esp32s3")
|
||||
list(APPEND srcs s3/hal_audio.c)
|
||||
list(APPEND priv_req board_jc4827w543)
|
||||
else()
|
||||
list(APPEND srcs stub/hal_audio.c)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
#include "board.h"
|
||||
|
||||
#define SPK_SAMPLE_RATE 48000
|
||||
#define TONE_HZ 2000
|
||||
#define TONE_PERIOD_SMP (SPK_SAMPLE_RATE / TONE_HZ) /* 24 samples */
|
||||
#define CHUNK_MS 10
|
||||
#define CHUNK_FRAMES (SPK_SAMPLE_RATE / 1000 * CHUNK_MS) /* 480, multiple of period */
|
||||
#define TONE_AMPLITUDE 12000
|
||||
#define DMA_DESC_NUM 4
|
||||
#define DMA_FRAME_NUM 240
|
||||
|
||||
static const char *TAG = "audio";
|
||||
|
||||
enum {
|
||||
AUDIO_SHORT = 1,
|
||||
AUDIO_ALARM,
|
||||
AUDIO_CANCEL,
|
||||
};
|
||||
|
||||
static QueueHandle_t s_q;
|
||||
static TaskHandle_t s_task;
|
||||
static i2s_chan_handle_t s_tx;
|
||||
static bool s_chan_on;
|
||||
static bool s_inited;
|
||||
|
||||
static int16_t s_tone[CHUNK_FRAMES * 2];
|
||||
static int16_t s_silence[CHUNK_FRAMES * 2];
|
||||
|
||||
static void chan_on(void)
|
||||
{
|
||||
if (!s_chan_on && s_tx != NULL) {
|
||||
i2s_channel_enable(s_tx);
|
||||
s_chan_on = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void chan_off(void)
|
||||
{
|
||||
if (s_chan_on && s_tx != NULL) {
|
||||
size_t w = 0;
|
||||
i2s_channel_write(s_tx, s_silence, sizeof(s_silence), &w, 50);
|
||||
i2s_channel_disable(s_tx);
|
||||
s_chan_on = false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool drain_cancel(void)
|
||||
{
|
||||
int msg;
|
||||
while (xQueueReceive(s_q, &msg, 0) == pdTRUE) {
|
||||
if (msg == AUDIO_CANCEL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Write buf for ~ms wall time. DMA pacing makes each 10 ms write block
|
||||
* in this task only; the UI path never touches I2S. */
|
||||
static bool play_buf_ms(const int16_t *buf, size_t len, uint32_t ms)
|
||||
{
|
||||
TickType_t t0 = xTaskGetTickCount();
|
||||
while ((xTaskGetTickCount() - t0) < pdMS_TO_TICKS(ms)) {
|
||||
if (drain_cancel()) {
|
||||
return false;
|
||||
}
|
||||
size_t w = 0;
|
||||
if (i2s_channel_write(s_tx, buf, len, &w, portMAX_DELAY) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void play_short(void)
|
||||
{
|
||||
chan_on();
|
||||
play_buf_ms(s_tone, sizeof(s_tone), 100);
|
||||
chan_off();
|
||||
}
|
||||
|
||||
static void play_alarm(void)
|
||||
{
|
||||
chan_on();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (!play_buf_ms(s_tone, sizeof(s_tone), 500)) {
|
||||
break;
|
||||
}
|
||||
if (!play_buf_ms(s_silence, sizeof(s_silence), 250)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
chan_off();
|
||||
}
|
||||
|
||||
static void audio_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
int msg;
|
||||
for (;;) {
|
||||
if (xQueueReceive(s_q, &msg, portMAX_DELAY) != pdTRUE) {
|
||||
continue;
|
||||
}
|
||||
if (msg == AUDIO_SHORT) {
|
||||
play_short();
|
||||
} else if (msg == AUDIO_ALARM) {
|
||||
play_alarm();
|
||||
} else if (msg == AUDIO_CANCEL) {
|
||||
chan_off();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void post(int msg)
|
||||
{
|
||||
if (s_q == NULL) {
|
||||
return;
|
||||
}
|
||||
xQueueSend(s_q, &msg, 0);
|
||||
}
|
||||
|
||||
void autofilm_audio_task(void *arg)
|
||||
{
|
||||
audio_task(arg);
|
||||
}
|
||||
|
||||
void hal_audio_init(void)
|
||||
{
|
||||
if (s_inited) {
|
||||
return;
|
||||
}
|
||||
gpio_num_t bclk = board_pin_spk_bclk();
|
||||
gpio_num_t lrclk = board_pin_spk_lrclk();
|
||||
gpio_num_t din = board_pin_spk_din();
|
||||
if (bclk == GPIO_NUM_NC || lrclk == GPIO_NUM_NC || din == GPIO_NUM_NC) {
|
||||
ESP_LOGW(TAG, "no speaker pins on this board");
|
||||
s_inited = true;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < CHUNK_FRAMES; i++) {
|
||||
int16_t v = ((i % TONE_PERIOD_SMP) < (TONE_PERIOD_SMP / 2))
|
||||
? TONE_AMPLITUDE : -TONE_AMPLITUDE;
|
||||
s_tone[i * 2] = v;
|
||||
s_tone[i * 2 + 1] = v;
|
||||
}
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
|
||||
chan_cfg.dma_desc_num = DMA_DESC_NUM;
|
||||
chan_cfg.dma_frame_num = DMA_FRAME_NUM;
|
||||
if (i2s_new_channel(&chan_cfg, &s_tx, NULL) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "i2s_new_channel failed");
|
||||
s_inited = true;
|
||||
return;
|
||||
}
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SPK_SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
|
||||
I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED,
|
||||
.bclk = bclk,
|
||||
.ws = lrclk,
|
||||
.dout = din,
|
||||
.din = I2S_GPIO_UNUSED,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
if (i2s_channel_init_std_mode(s_tx, &std_cfg) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "i2s_channel_init_std_mode failed");
|
||||
i2s_del_channel(s_tx);
|
||||
s_tx = NULL;
|
||||
s_inited = true;
|
||||
return;
|
||||
}
|
||||
s_q = xQueueCreate(8, sizeof(int));
|
||||
if (s_task == NULL) {
|
||||
xTaskCreate(audio_task, "audio", 3072, NULL, 2, &s_task);
|
||||
}
|
||||
s_inited = true;
|
||||
ESP_LOGI(TAG, "init i2s bclk=%d ws=%d dout=%d", bclk, lrclk, din);
|
||||
}
|
||||
|
||||
void hal_audio_beep_short(void)
|
||||
{
|
||||
post(AUDIO_SHORT);
|
||||
}
|
||||
|
||||
void hal_audio_alarm_complete(void)
|
||||
{
|
||||
post(AUDIO_ALARM);
|
||||
}
|
||||
|
||||
void hal_audio_alarm_cancel(void)
|
||||
{
|
||||
post(AUDIO_CANCEL);
|
||||
}
|
||||
@@ -3,8 +3,11 @@ set(priv_inc)
|
||||
set(priv_req driver)
|
||||
|
||||
if(IDF_TARGET STREQUAL "esp32")
|
||||
list(APPEND srcs wroom/hal_motor.c)
|
||||
list(APPEND srcs hal_motor.c)
|
||||
list(APPEND priv_req board_wroom)
|
||||
elseif(IDF_TARGET STREQUAL "esp32s3")
|
||||
list(APPEND srcs hal_motor.c)
|
||||
list(APPEND priv_req board_jc4827w543)
|
||||
else()
|
||||
list(APPEND srcs stub/hal_motor.c)
|
||||
endif()
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#define PIN_STEP 12
|
||||
#define PIN_DIR 14
|
||||
#define PIN_EN 27
|
||||
#include "board.h"
|
||||
#include "hal_motor.h"
|
||||
|
||||
#define STEPS_PER_REV 4800
|
||||
#define DEFAULT_RPM 60
|
||||
#define ACCEL 9600
|
||||
@@ -23,6 +23,10 @@
|
||||
|
||||
static const char *TAG = "motor";
|
||||
|
||||
static gpio_num_t s_pin_en = GPIO_NUM_NC;
|
||||
static gpio_num_t s_pin_dir = GPIO_NUM_NC;
|
||||
static gpio_num_t s_pin_step = GPIO_NUM_NC;
|
||||
static int s_en_disable_level = 1;
|
||||
static TaskHandle_t s_task;
|
||||
static atomic_uint s_stop_req;
|
||||
static float s_cw;
|
||||
@@ -35,13 +39,17 @@ static rmt_encoder_handle_t s_enc;
|
||||
|
||||
static void en_disable(void)
|
||||
{
|
||||
gpio_set_level((gpio_num_t)PIN_EN, 1);
|
||||
if (s_pin_en != GPIO_NUM_NC) {
|
||||
gpio_set_level(s_pin_en, s_en_disable_level);
|
||||
}
|
||||
s_enabled = false;
|
||||
}
|
||||
|
||||
static void en_enable(void)
|
||||
{
|
||||
gpio_set_level((gpio_num_t)PIN_EN, 0);
|
||||
if (s_pin_en != GPIO_NUM_NC) {
|
||||
gpio_set_level(s_pin_en, !s_en_disable_level);
|
||||
}
|
||||
s_enabled = true;
|
||||
}
|
||||
|
||||
@@ -111,7 +119,7 @@ static bool run_move(long steps)
|
||||
if (steps < 0) {
|
||||
steps = -steps;
|
||||
}
|
||||
gpio_set_level((gpio_num_t)PIN_DIR, dir);
|
||||
gpio_set_level(s_pin_dir, dir);
|
||||
|
||||
uint32_t cruise = (s_rpm * STEPS_PER_REV) / 60;
|
||||
if (cruise == 0) {
|
||||
@@ -189,22 +197,31 @@ void hal_motor_init(void)
|
||||
if (s_inited) {
|
||||
return;
|
||||
}
|
||||
s_pin_en = board_pin_motor_en();
|
||||
s_pin_dir = board_pin_motor_dir();
|
||||
s_pin_step = board_pin_motor_step();
|
||||
s_en_disable_level = board_motor_en_disable_level();
|
||||
if (s_pin_en == GPIO_NUM_NC || s_pin_dir == GPIO_NUM_NC || s_pin_step == GPIO_NUM_NC) {
|
||||
ESP_LOGW(TAG, "no motor pins on this board");
|
||||
s_inited = true;
|
||||
return;
|
||||
}
|
||||
gpio_config_t io = {
|
||||
.pin_bit_mask = (1ULL << PIN_EN) | (1ULL << PIN_DIR),
|
||||
.pin_bit_mask = (1ULL << s_pin_en) | (1ULL << s_pin_dir),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
gpio_config(&io);
|
||||
gpio_set_level((gpio_num_t)PIN_EN, 1);
|
||||
gpio_set_level((gpio_num_t)PIN_DIR, 0);
|
||||
gpio_set_level(s_pin_en, s_en_disable_level);
|
||||
gpio_set_level(s_pin_dir, 0);
|
||||
s_enabled = false;
|
||||
atomic_store(&s_stop_req, 0);
|
||||
|
||||
rmt_tx_channel_config_t txcfg = {
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT,
|
||||
.gpio_num = PIN_STEP,
|
||||
.gpio_num = s_pin_step,
|
||||
.mem_block_symbols = 64,
|
||||
.resolution_hz = RMT_RES_HZ,
|
||||
.trans_queue_depth = 4,
|
||||
@@ -219,7 +236,8 @@ void hal_motor_init(void)
|
||||
configMAX_PRIORITIES - 2, &s_task, 0);
|
||||
}
|
||||
s_inited = true;
|
||||
ESP_LOGI(TAG, "init EN=HIGH RMT step=%d dir=%d", PIN_STEP, PIN_DIR);
|
||||
ESP_LOGI(TAG, "init en=%d step=%d dir=%d dis_lvl=%d",
|
||||
s_pin_en, s_pin_step, s_pin_dir, s_en_disable_level);
|
||||
}
|
||||
|
||||
void hal_motor_enable(bool on)
|
||||
@@ -234,8 +252,7 @@ void hal_motor_enable(bool on)
|
||||
void hal_motor_request_stop(void)
|
||||
{
|
||||
atomic_store(&s_stop_req, 1);
|
||||
gpio_set_level((gpio_num_t)PIN_EN, 1);
|
||||
s_enabled = false;
|
||||
en_disable();
|
||||
rmt_abort();
|
||||
if (s_task != NULL) {
|
||||
xTaskNotifyGive(s_task);
|
||||
@@ -3,8 +3,11 @@ set(priv_inc)
|
||||
set(priv_req driver)
|
||||
|
||||
if(IDF_TARGET STREQUAL "esp32")
|
||||
list(APPEND srcs wroom/hal_temp.c)
|
||||
list(APPEND srcs hal_temp.c)
|
||||
list(APPEND priv_req board_wroom onewire_bus)
|
||||
elseif(IDF_TARGET STREQUAL "esp32s3")
|
||||
list(APPEND srcs hal_temp.c)
|
||||
list(APPEND priv_req board_jc4827w543 onewire_bus)
|
||||
else()
|
||||
list(APPEND srcs stub/hal_temp.c)
|
||||
endif()
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "onewire_bus.h"
|
||||
#include "board.h"
|
||||
#include "hal_temp.h"
|
||||
|
||||
#define PIN_DS 13
|
||||
#define TEMP_OFFSET 0.4f
|
||||
#define CMD_SKIP_ROM 0xCC
|
||||
#define CMD_CONVERT_T 0x44
|
||||
@@ -40,7 +41,7 @@ static bool scratch_valid(const uint8_t *sp)
|
||||
return crc == sp[8];
|
||||
}
|
||||
|
||||
void autofilm_temp_tick(void)
|
||||
void hal_temp_tick(void)
|
||||
{
|
||||
if (s_bus == NULL) {
|
||||
s_last_ok = false;
|
||||
@@ -84,21 +85,19 @@ void autofilm_temp_tick(void)
|
||||
s_last_ok = true;
|
||||
}
|
||||
|
||||
void autofilm_temp_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
for (;;) {
|
||||
autofilm_temp_tick();
|
||||
}
|
||||
}
|
||||
|
||||
void hal_temp_init(void)
|
||||
{
|
||||
if (s_inited) {
|
||||
return;
|
||||
}
|
||||
gpio_num_t pin = board_pin_temp();
|
||||
if (pin == GPIO_NUM_NC) {
|
||||
ESP_LOGW(TAG, "no temp pin on this board");
|
||||
s_inited = true;
|
||||
return;
|
||||
}
|
||||
onewire_bus_config_t bus_config = {
|
||||
.bus_gpio_num = PIN_DS,
|
||||
.bus_gpio_num = pin,
|
||||
.flags = {
|
||||
.en_pull_up = true,
|
||||
},
|
||||
@@ -108,12 +107,12 @@ void hal_temp_init(void)
|
||||
};
|
||||
if (onewire_new_bus_rmt(&bus_config, &rmt_config, &s_bus) != ESP_OK) {
|
||||
s_bus = NULL;
|
||||
ESP_LOGE(TAG, "onewire_bus install failed pin=%d", PIN_DS);
|
||||
ESP_LOGE(TAG, "onewire_bus install failed pin=%d", pin);
|
||||
}
|
||||
s_last_ok = false;
|
||||
s_last_c = 0.0f;
|
||||
s_inited = true;
|
||||
ESP_LOGI(TAG, "init onewire_bus pin=%d offset=%.1f", PIN_DS, (double)TEMP_OFFSET);
|
||||
ESP_LOGI(TAG, "init onewire_bus pin=%d offset=%.1f", pin, (double)TEMP_OFFSET);
|
||||
}
|
||||
|
||||
esp_err_t hal_temp_read_c(float *out)
|
||||
@@ -7,4 +7,5 @@
|
||||
#endif
|
||||
|
||||
void hal_temp_init(void);
|
||||
void hal_temp_tick(void); /* blocking conversion poll; blocks >=750 ms */
|
||||
esp_err_t hal_temp_read_c(float *out); /* ESP_FAIL → disconnected */
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
#include "hal_temp.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
void hal_temp_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void hal_temp_tick(void)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(750));
|
||||
}
|
||||
|
||||
esp_err_t hal_temp_read_c(float *out)
|
||||
{
|
||||
(void)out;
|
||||
|
||||
@@ -36,6 +36,6 @@ If blocked: stop, commit nothing broken, write `BLOCKED:` at top of the phase fi
|
||||
| ID | File | Session goal | STATUS |
|
||||
| --- | --- | --- | --- |
|
||||
| I01 | `integration/I01-board-configs.md` | Board pin allocations & accessors | DONE |
|
||||
| I02 | `integration/I02-temp-fix.md` | Temp HAL shared & starvation fix | TODO |
|
||||
| I03 | `integration/I03-motor-hal.md` | Motor HAL shared across boards | TODO |
|
||||
| I04 | `integration/I04-audio-i2s.md` | I2S audio implementation for S3 | TODO |
|
||||
| I02 | `integration/I02-temp-fix.md` | Temp HAL shared & starvation fix | DONE |
|
||||
| I03 | `integration/I03-motor-hal.md` | Motor HAL shared across boards | DONE |
|
||||
| I04 | `integration/I04-audio-i2s.md` | I2S audio implementation for S3 | DONE |
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
STATUS: TODO
|
||||
STATUS: DONE
|
||||
DEPENDS: I01
|
||||
Notes: Poll API named `hal_temp_tick` (declared in hal_temp.h). `idf.py build` for both targets verified via CI run 38616 (no local IDF). S3 flash watchdog check not run — no device attached to this machine.
|
||||
|
||||
**READ:** `docs/megaplans/INTEGRATION-MEGAPLAN.md`, `main/main.c`, `components/hal_temp/*`
|
||||
|
||||
@@ -17,6 +18,6 @@ DEPENDS: I01
|
||||
2. `Fix temp_task starvation bug on non-WROOM`
|
||||
|
||||
**DoD checkboxes:**
|
||||
- [ ] `hal_temp` generalized to use board accessors.
|
||||
- [ ] `temp_task` starvation bug fixed.
|
||||
- [ ] Build succeeds on both targets.
|
||||
- [x] `hal_temp` generalized to use board accessors.
|
||||
- [x] `temp_task` starvation bug fixed.
|
||||
- [x] Build succeeds on both targets.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
STATUS: TODO
|
||||
STATUS: DONE
|
||||
DEPENDS: I01
|
||||
|
||||
**READ:** `docs/megaplans/INTEGRATION-MEGAPLAN.md`, `components/hal_motor/*`
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
STATUS: TODO
|
||||
STATUS: DONE
|
||||
DEPENDS: I01
|
||||
Notes: `idf.py build` for both targets verified via CI run 38684 (no local IDF).
|
||||
|
||||
**READ:** `docs/megaplans/INTEGRATION-MEGAPLAN.md`, `components/hal_audio/*`
|
||||
|
||||
@@ -17,6 +18,6 @@ DEPENDS: I01
|
||||
1. `Add I2S speaker audio for S3`
|
||||
|
||||
**DoD checkboxes:**
|
||||
- [ ] `hal_audio` I2S implementation added for S3.
|
||||
- [ ] Queue protocol and non-blocking semantics maintained.
|
||||
- [ ] Build succeeds.
|
||||
- [x] `hal_audio` I2S implementation added for S3.
|
||||
- [x] Queue protocol and non-blocking semantics maintained.
|
||||
- [x] Build succeeds.
|
||||
|
||||
+1
-5
@@ -28,8 +28,6 @@ static const char *TAG = "app";
|
||||
#if defined(AUTOFILM_BOARD_WROOM) || defined(AUTOFILM_BOARD_S3)
|
||||
static QueueHandle_t s_cmdq;
|
||||
|
||||
extern void autofilm_temp_tick(void);
|
||||
|
||||
static void map_and_post_key(char key)
|
||||
{
|
||||
ui_cmd_t cmd;
|
||||
@@ -168,9 +166,7 @@ static void temp_task(void *arg)
|
||||
(void)arg;
|
||||
esp_task_wdt_add(NULL);
|
||||
for (;;) {
|
||||
#ifdef AUTOFILM_BOARD_WROOM
|
||||
autofilm_temp_tick();
|
||||
#endif
|
||||
hal_temp_tick();
|
||||
float c = 0.0f;
|
||||
bool ok = (hal_temp_read_c(&c) == ESP_OK);
|
||||
app_machine_on_temp(ok ? c : 0.0f, ok);
|
||||
|
||||
@@ -10,6 +10,10 @@ void hal_temp_init(void)
|
||||
stub_temp_c = 20.0f;
|
||||
}
|
||||
|
||||
void hal_temp_tick(void)
|
||||
{
|
||||
}
|
||||
|
||||
esp_err_t hal_temp_read_c(float *out)
|
||||
{
|
||||
if (stub_temp_fail) {
|
||||
|
||||
Reference in New Issue
Block a user