feat: use cpp version of common drivers - #5
Conversation
|
This is temporary, and is a temporary way to test the cpp changes in Common in a quick and dirty way. Hence why I piggybacked off your cpp commits and stuff everything in a class. I would like to move Tasks into Common personally, but this needs more design and discussion. |
|
I agree with moving tasks into common, even if they are only used on one device. It just makes things easier to share. It may make sense though to have some kind of interface or functions that are not defined on the platform side that can be easily defined on the device side. For example, if we have a buzzer task, the pin and timer we want to use may change so that implementation may be on the device side. |
ce9e4a2 to
ccc187e
Compare
|
make sure to commit the bumped submodule version, that is causing the build fail. |
| #include "hal.h" | ||
|
|
||
| /** | ||
| * @brief device init |
| class TEST_CLASS { | ||
| private: | ||
| CRC_HandleTypeDef &hcrc; | ||
|
|
||
| // Peripherals | ||
| Platform::SPI accel; | ||
| Platform::SPI gyro; | ||
| Platform::SPI bmp; | ||
| Platform::SPI flash; | ||
|
|
||
| // Sensors and Flash | ||
| Platform::GD5F1GQ5XE flash_dev; | ||
| Platform::BMP581 bmp581_dev; | ||
| Platform::BMI088 bmi088_dev; | ||
| std::array<Platform::Sensor*, NUM_SENSORS> sensors; | ||
|
|
||
| // Runtime State | ||
| SemaphoreHandle_t packet_mutex = xSemaphoreCreateMutex(); | ||
| Platform::Packet packet = Platform::Packet(); | ||
| volatile bool save_to_flash = false; | ||
| lfs_file_t packet_file; | ||
|
|
||
| int32_t fs_size = 0; | ||
| uint32_t boot_count = 0; | ||
| uint32_t file_size = 0; | ||
|
|
||
| // Task attributes | ||
| TaskHandle_t sensor_task_handle = NULL; | ||
| TaskHandle_t flash_task_handle = NULL; | ||
| // Tasks | ||
| static void sensor_task(void *argument) { | ||
| auto *self = static_cast<TEST_CLASS*>(argument); | ||
| self->sensor_loop(); | ||
| } | ||
|
|
||
| static void flash_task(void *argument) { | ||
| auto *self = static_cast<TEST_CLASS*>(argument); | ||
| self->flash_loop(); | ||
| } | ||
| public: | ||
| TEST_CLASS(SPI_HandleTypeDef &hspi1, SPI_HandleTypeDef &hspi2, SPI_HandleTypeDef &hspi3, CRC_HandleTypeDef &hcrc) | ||
| : hcrc(hcrc), | ||
| accel(&hspi1, IMU2_ACC_CS_GPIO_Port, IMU2_ACC_CS_Pin), | ||
| gyro(&hspi1, IMU2_GYRO_CS_GPIO_Port, IMU2_GYRO_CS_Pin), | ||
| bmp(&hspi2, BAR1_CS_GPIO_Port, BAR1_CS_Pin), | ||
| flash(&hspi3, FLASH_CS_GPIO_Port, FLASH_CS_Pin), | ||
|
|
||
| flash_dev(flash), | ||
| bmp581_dev(bmp), | ||
| bmi088_dev(accel, gyro), | ||
| sensors{&bmp581_dev, &bmi088_dev} | ||
| { | ||
| // Initialize sensors | ||
| for (auto& sensor : sensors) { | ||
| bool ready = 0; | ||
| for (int c = 0; c < 10; ++c) { | ||
| ready = sensor->init(); | ||
| if (ready) break; | ||
| HAL_Delay(20000); | ||
| } | ||
| if (!ready) sensor = nullptr; | ||
| } | ||
|
|
||
| // Initialize flash | ||
| for (int c = 0; c < 3; ++c) { | ||
| if (flash_dev.init()) { | ||
| fs_size = flash_dev.mount(); | ||
| if (fs_size >= 0) { | ||
| save_to_flash = true; | ||
| boot_count = flash_dev.bootcount(false); | ||
| file_size = flash_dev.open(&packet_file, "packets"); | ||
| } | ||
| break; | ||
| } | ||
| HAL_Delay(5000); | ||
| } | ||
| Platform::log("Flash: %u (size), %u (boot), %u (packet_size)\r\n", fs_size, boot_count, file_size); | ||
| } |
There was a problem hiding this comment.
I still don't really like this method of setting things up. I'd rather see tasks completely isolated rather than share a class for initialization. This is kinda the setup I have in mind:
In platform:
sensor task and init high level is written here. Shouldn't really have device specific code, can assume FreeRTOS and STM32 is used though.
In Hephaestus:
Any device specific functions needed for the task. This might be where we do stuff with specific pins for example, you use an LED for the sensor loop.
In the actual device_init function we would then have FlashTask::Init() which would handle the scheduling of the task. The idea here is to keep device-init.cpp clean, and to keep applications compartmentalized.
There was a problem hiding this comment.
Lets get the cpp-migration merged first for this and common-drivers, then work on the task abstraction. Mahir is gonna yell at me if I stuff more abstractions in...
There was a problem hiding this comment.
fair enough, that's fine with me
There was a problem hiding this comment.
I agree with most of Nathan's proposal here, I also think tasks should be isolated units. However, I disagree with moving the Sensor Task into common. The sensors that each board is polling will be different, so a common Sensor Task will basically become a become a god class/object, not ideal.
There was a problem hiding this comment.
How does that look though? If we made an individual sensor task for every sensor, we'd have a lot of boilerplate. Additionally we want to read all sensors at the same time to ensure the timestamp is accurate for everything. I think we can make a truly general sensor task by making it take the list of sensors on init and having a couple functions that are defined for each device? Some of those might be things like NotifyFlashTask and GetTimestamp.
There was a problem hiding this comment.
The current sensor task just does this
for (auto *sensor : sensors)
if (sensor != nullptr) sensor->read(packet);
It takes a list of the sensors it wants and just loop over it. It doesn't really care about what sensors are available, just what sensors is currently in the array
There was a problem hiding this comment.
Of course it also does a bit of other stuff like semaphores, checksums, and notifying the flash task. Anyways, I did want more discussion on task abstractions which is why I wanted to punt this to another PR.
There was a problem hiding this comment.
@ncorrea210 You misunderstood what I wrote. I was not saying to have a task for each sensor. Doesn't matter anyways since the way I was thinking the task would've been implemented was a bit foolish anyways, what Ivan just wrote makes sense so I can see how it can be in common,
| void disable_flash() { | ||
| if (save_to_flash) { | ||
| save_to_flash = false; | ||
| vTaskNotifyGiveFromISR(flash_task_handle, NULL); | ||
| } | ||
| } |
There was a problem hiding this comment.
is this function guaranteed to only be called in an ISR context? It can be dangerous to use FromISR functions in a non-ISR context. See this.
There was a problem hiding this comment.
Yes, this is guaranteed to only be called from ISR, similarly non-ISR functions should not be called in ISR-context so it goes both ways.
| xTaskCreate(flash_task, "flash task", 512, this, 1, &flash_task_handle); | ||
| xTaskCreate(sensor_task, "sensor task", 256, this, 2, &sensor_task_handle); |
There was a problem hiding this comment.
Might be good to look into using xTaskCreateStatic so there aren't any heap allocations. Probably not super important though.
Note that this requires the cpp branch of CommonDrivers to be merged
ccc187e to
60fd547
Compare
Very very messy, there are a lot of things which I think can be done better.