*************
C++
topic: 嵌入式
*************
之前做了很多的代码题目,在LeetCode上。在嵌入式开发上,其实不需要用到很多算法,往往一些简单的代码就可以解决很多的问题。
比如,我需要读取控制板上的电流和电压,用于判断电机的运转工况,用到的芯片是上文提到的KV4x的。那么代码就很简单。接下来按照步骤进行到代码的创作。
最重要的,也是最开始的,了解芯片的ADC模块,analog digital converter,通道和配置。这个在商品文章中已经深入的学习过了。
- 在主板上确认电流和电压接到哪些引脚,引脚对应的ADC的通道是什么 。
- 配置ADC模块,设置分辨率、参考电压、采样速率等单数。
- 配置GPIO,把连接电流和电压的引脚设置为模拟输入模式。
- 对于电流的测量,可能需要一个电流感应电阻,把电流转换成电压,再通过ADC读取。
- 电压的测量直接通过分压电阻网络,把电压降到ADC的输入范围内。
- 然后,写代码初始化ADC,配置相关寄存器。
- 写函数来读取ADC的值,转换成实际的电流和电压数值。
- 需要处理采样平均,滤波,提高数据的准确性。
- 最后,测试一下,看看读取的值是否合理,和实际测量的值对比一下。
看一下简化的代码:
#include "KV4XP100M168.h"
#include "adc.h"
#include "gpio.h"
// 配置ADC
void ADC_Init() {
// 配置ADC时钟
// 使能ADC模块
// 设置参考电压
// 设置分辨率
// 配置ADC通道
// 启动ADC
}
// 读取ADC值
uint16_t ADC_Read(uint8_t channel) {
// 选择通道
// 启动转换
// 等待转换完成
// 读取转换结果
return adc_value;
}
// 主函数
int main() {
// 初始化系统时钟
SystemClock_Config();
// 初始化GPIO
GPIO_Init();
// 初始化ADC
ADC_Init();
while (1) {
// 读取电压通道的ADC值
uint16_t voltage_adc = ADC_Read(VOLTAGE_CHANNEL);
// 转换为实际电压值
float voltage = voltage_adc * (V_REF / (float)(ADC_RESOLUTION - 1));
// 读取电流通道的ADC值
uint16_t current_adc = ADC_Read(CURRENT_CHANNEL);
// 转换为实际电流值
float current = (current_adc * (V_REF / (float)(ADC_RESOLUTION - 1))) / CURRENT_SHUNT_RESISTANCE;
// 处理电压和电流数据
// ...
// 延时
Delay_ms(100);
}
}
对于配置ADC模块,简化的代码如下:
#include "KV4XP100M168.h"
#include "adc.h"
#include "gpio.h"
#include <stdint.h>
// 定义常量
#define V_REF 3.3f // 参考电压,假设是3.3V
#define ADC_RESOLUTION 4095 // 12位ADC
#define CURRENT_SHUNT_RESISTANCE 0.1f // 电流检测电阻,0.1欧姆
#define VOLTAGE_CHANNEL 0 // 电压测量通道,假设是通道0
#define CURRENT_CHANNEL 1 // 电流测量通道,假设是通道1
// ADC初始化函数
void ADC_Init(void) {
// 使能ADC0时钟
SIM->SCGCADC |= SIM_SCGCADC_ADC0_CLK_ENABLE;
// 选择内部参考电压
ADC0->RGMC1 |= ADC_RGMC1_VREFEN_MASK;
// 设置12位分辨率
ADC0->RGMC1 &= ~ADC_RGMC1_MODE_MASK;
// 配置GPIO引脚为模拟输入模式
// 假设电压通道是GPIOA第0脚,电流通道是GPIOA第1脚
GPIOA->PDDR &= ~((1 << 0) | (1 << 1)); // 确保引脚不是推挽输出
GPIOA->GCR |= (1 << 0) | (1 << 1); // 使能模拟功能
// 其他配置,如采样时间、序列等
// ...
}
// 读取ADC值的函数
uint16_t ADC_Read(uint8_t channel) {
// 选择通道
ADC0->RGMCTL = (ADC0->RGMCTL & ~ADC_RGMCTL_RGMCHAN_MASK) | (channel << ADC_RGMCTL_RGMCHAN_SHIFT);
// 启动转换
ADC0->RGMCTL |= ADC_RGMCTL_RGMSTART_MASK;
// 等待转换完成
while ((ADC0->RGMSTAT & ADC_RGMSTAT_RGMEOCF_MASK) == 0);
// 读取转换结果
uint16_t adc_value = ADC0->RGMDAT0;
// 清除转换完成标志
ADC0->RGMSTAT |= ADC_RGMSTAT_RGMEOCF_MASK;
return adc_value;
}
// 延时函数
void Delay_ms(uint32_t ms) {
for (uint32_t i = 0; i < ms; i++) {
for (uint32_t j = 0; j < 1000; j++);
}
}
// 主函数
int main(void) {
// 初始化系统时钟
SystemClock_Init();
// 初始化ADC
ADC_Init();
while (1) {
// 读取电压通道的ADC值
uint16_t voltage_adc = ADC_Read(VOLTAGE_CHANNEL);
// 读取电流通道的ADC值
uint16_t current_adc = ADC_Read(CURRENT_CHANNEL);
// 转换为实际电压和电流值
float voltage = (voltage_adc * V_REF) / (float)ADC_RESOLUTION;
float current = (current_adc * V_REF) / (float)(CURRENT_SHUNT_RESISTANCE * ADC_RESOLUTION);
// 处理电压和电流数据
// ...
// 延时
Delay_ms(100);
}
}
代码其实很简单,这些代码比LeetCode上的题目简单多了,比较困难的是系统的对芯片进行设置,要结合相应的电路和系统的处理关系。代码只是工具,不能单出,代码加上系统控制,那就比较牛逼了。