2024-2-20
Kotlin
混淆(Android/Kotlin+Java)
参见 stackoverflow.com/a/50819864。… app 必须也开启混淆才能有效。如果主模块开启了混淆但子模块没有开启,子模块也会被混淆。换言之,主模块的混淆开关会完全覆盖子模块的混淆开关。
对于子模块,混淆规则通常要写在 consumer-rules 里,因为 proguard-rules 是被当作单独发布的模块时才被使用的,一般并不这么做。
2024-2-23
C
STM32 ADC, PWM, TIM(STM32/C)
STM32 中,管脚要作为 ADC 工作,需要选择 ADCx_INy 模式。要轮询工作,可以用 Start、PollForConversion、GetValue、Stop。
const uint32_t INFINITY = 0xFFFFFFFF;
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, INFINITY);
const uint32_t value = HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(&hadc1); // 实践表明这是可选的。
注意 ADC 可能只有一个,但通道有很多个。多个通道都需要采样时,只能依次进行。
PWM 实际是 TIM 提供的能力。管脚要作为 PWM 工作,需要选择 PWMx_CHy 模式,对应 TIM 的通道要选择 PWM Generation CHy 模式。设置占空比实际上是设置比较值,HAL 库提供了一个宏 __HAL_TIM_SET_COMPARE 可以一键设置。
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, counter_target);
本质是直接设置寄存器。
#define __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) \
(((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCR1 = (__COMPARE__)) :\
((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCR2 = (__COMPARE__)) :\
((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCR3 = (__COMPARE__)) :\
((__HANDLE__)->Instance->CCR4 = (__COMPARE__)))
TIM 用作定时,需要将通道设置为 Output Compare No Output 模式。周期是 (Prescaler + 1) * (CounterPeriod + 1) 个时钟周期,时钟取决于 APB1 和 APB2,具体参见手册,看 TIMx 的时钟频率对应谁。
TIM 用作定时的中断通知函数名为 HAL_TIM_PeriodElapsedCallback。下面的代码只区分了一组定时器,没有区分各个通道。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == &htim1) {
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
}
}
对于高级定时器,要激活以上中断,需要启用 TIM update interrupt 中断。普通定时器启用 TIM global interrupt 即可。
2024-2-24
C
STM32 I2C(STM32/C)
使用 STM32 的 HAL 库实现 Transmit 后不发 stop condition 并紧接着 Receive,需要用到 Seq 系列函数。Seq 系列函数不提供阻塞模式,只有中断和 DMA 模式。使用中断模式时,需要打开中断事件开关(I2C event interrupt)。为了方便,把任务交给中断处理后,也可以轮询等待传输结束。轮询代码需要手动编写,方法是检查 I2C 的状态是否为 Ready。
/* Send device address, with no STOP condition */
ret = HAL_I2C_Master_Seq_Transmit_IT(_hi2c, _address, (uint8_t*)&RegisterAddr, 1, I2C_LAST_FRAME_NO_STOP);
if(ret == HAL_OK) {
/* Read data, with STOP condition */
while (HAL_I2C_GetState(_hi2c) != HAL_I2C_STATE_READY)
;
ret = HAL_I2C_Master_Seq_Receive_IT(_hi2c, _address, pBuffer, NumByteToRead, I2C_LAST_FRAME);
while (HAL_I2C_GetState(_hi2c) != HAL_I2C_STATE_READY)
;
}
参见 blog.csdn.net/NeoZng/arti… 系列函数通过指定额外参数,可以条件 I2C 单帧传输的具体时序。
可变参数(所有平台/C)
C 语言中的可变参数通常结合 vsprintf(或 vsscanf)使用。需要包含头文件:
#include <stdarg.h>
#include <stdio.h>
关键类型为 va_list,关键函数为 va_start、vsprintf、va_end。
int huart2_printf(const char* format, ...) {
static char buf[256];
int ret;
va_list list;
va_start(list, format);
ret = vsprintf(buf, format, list);
va_end(list);
const uint32_t INFINITY = 0xFFFFFFFF;
HAL_UART_Transmit(&huart2, (const uint8_t*)buf, (uint16_t)ret, INFINITY);
return ret;
}
C 语言中宏的可变参数用 __VA_ARGS__ 引用。
#define printf(...) huart2_printf(__VA_ARGS__)
2024-2-26
Android Studio
配置 JDK
参见 stackoverflow.com/a/76655993。… -> Build, Execution, Deployment -> Gradle 中,选择 Gradle JDK 下拉列表框,可以点击里面的 Download JDK 自动下载 JDK。
2024-2-27
硬件
布线
双面板可以其中一面全部铺地。铺地时尽可能保证地平面的完整。
2024-2-28
Kotlin
SharedPreferences(Android/Kotlin+Java)
参见 juejin.cn/post/684490… 可以做轻量级的配置存储,底层原理是将所有配置保存为单个 XML 文件。其内部有一系列机制保证数据完整性,之后再学习。