2253|2

155

帖子

1

TA的资源

一粒金砂(高级)

楼主
 

【Luckfox幸狐 RV1106 Linux 开发板测评】八、ADC和UART测试 [复制链接]

 

本人【Luckfox幸狐 RV1106 Linux 开发板测评】帖子链接:

一、开箱及测试

二、SDK获取与编译镜像

三、GPIO点灯

四、通过PC机共享网络接入Internet和Ubuntu下Python点灯

五、编译Buildroot系统并测试摄像头

六、PWM控制——命令和C方式

七、PWM控制——Python和设备树方式

 

本篇继续测试Luckfox Pro Max板子的外围接口:ADC和UART——只测试了UART3。前面已经依照文档测评了GPIO和PWM输出,证明文档没有什么坑,所以本篇直接将ADC和UART3的控制合并到一起,编写一个“UART3接收命令,然后根据命令开启一次ADC转换”的实验程序。

 

 

图8-1 UART3连接和ADC管脚

 

板子串口启用UART3,19脚Tx,20脚Rx,有两个ADC(0/1)为31,32脚。串口通过USB-TTL连接到PC机,串口调试工具使用XCOM。

开发板的/sys/bus/iio/devices/iio\:device0/目录下是ADC设备的属性文件,主要操作三个:in_voltage_scale用于获取ADC读数和实际电压的转换因子,in_voltage0_raw用于获取ADC通道0读数,in_voltage1_raw用于获取ADC通道1读数。

开发板的/dev目录下包含UART相关的设备文件,UART3对应文件/dev/ttyS3。

1、Python方式

Python控制程序中,ADC没有使用库,直接进行文件操作,UART使用了serial库,需要注意的串口的读写函数对应参数都是bytes类型,尤其是调用serial.write()进行串口输出时,输出内容是字符串需要通过encode()函数转为bytes。具体代码如下:

import serial  # 导入串行通信库
import time    # 导入时间库,用于延时

# 初始化串行端口uart3,配置其参数
uart3 = serial.Serial(
  "/dev/ttyS3",  # 串行端口的设备文件路径
  baudrate=115200,  # 波特率,即每秒传输的位数
  bytesize=serial.EIGHTBITS,  # 数据位大小,这里是8位
  stopbits=serial.STOPBITS_ONE,  # 停止位数量,这里是1位
  parity=serial.PARITY_NONE,  # 校验位,这里不使用校验
  timeout=1,  # 读取超时时间,单位是秒
)

# ADC设备的路径
ADC_DIR = "/sys/bus/iio/devices/iio:device0"

# 定义用于从指定文件路径读取值的函数
def read_value(file_path):
  with open(file_path, "r") as file:  # 打开文件
    return file.read().strip()  # 读取文件内容并去除前后的空白字符

# 主函数
def main():
  print("Press Ctrl+C to quit")  # 提示用户如何退出程序
  while True:  # 无限循环
    buf = uart3.read(128)  # 从串行端口读取最多128个字节的数据
    if b"ADC0" in buf:  # 如果接收到的数据中包含"ADC0"
      # 从ADC设备读取相关的值
      scale_value = float(read_value(f"{ADC_DIR}/in_voltage_scale"))  # 读取电压比例因子
      IN0_raw_value = float(read_value(f"{ADC_DIR}/in_voltage0_raw"))  # 读取ADC0的原始值
      # 计算ADC0的电压值并格式化输出
      IN0_voltage = f"{IN0_raw_value * scale_value / 1000:.2f}"
      print(f"IN0_Voltage: {IN0_voltage} V")  # 打印到控制台
      uart3.write(f"IN0_Voltage: {IN0_voltage} V\n".encode('utf-8'))  # 通过串行端口发送数据
    elif b"ADC1" in buf:  # 如果接收到的数据中包含"ADC1"
      # 类似地,处理ADC1的数据
      scale_value = float(read_value(f"{ADC_DIR}/in_voltage_scale"))
      IN1_raw_value = float(read_value(f"{ADC_DIR}/in_voltage1_raw"))
      IN1_voltage = f"{IN1_raw_value * scale_value / 1000:.2f}"
      print(f"IN1_Voltage: {IN1_voltage} V")
      uart3.write(f"IN1_Voltage: {IN1_voltage} V\n".encode('utf-8'))  # encode转bytes
    time.sleep(2)  # 等待2秒

if __name__ == "__main__":
  try:
    main()  # 调用主函数
  except KeyboardInterrupt:  # 如果用户按下Ctrl+C
    uart3.close()  # 关闭串行端口
    pass  # 什么都不做,继续退出程序

 

 

图8-2 adc.py执行效果

 

程序执行效果如上图所示,XCOM连接板子UART3,发送“ADC0”过去则控制开发板对通道0进行一次ADC读值,发送“ADC1”则控制通道1。这里为了省事,两个ADC管脚都是悬空的,看输出应该是悬空为“高”——看文档ADC管脚参考电压就是1.8V。

2、C方式

C语音程序也是以操作设备文件的方式进行ADC和UART控制,其中需要用到struct termios结构体,这个结构体是在 POSIX 规范中定义的一个标准接口,它类似于系统 V 中的 termio 接口。这个结构体用于控制非同步通信端口,提供了一个常规的终端接口。通过设置 termios 类型的数据结构中的值和使用一组函数调用,你可以对终端接口进行控制。而在 Linux 系统中,struct termios 结构体常用于串口编程,用于设置串口参数、读取和写入串口数据等操作。通过调整这个结构体的各个成员,你可以控制串口的各种属性,如波特率、数据位、停止位、校验位等,以及终端的输入输出行为。相关代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>

// 定义一个串口发送函数,增加发送出错的判断
int writeBuf(int fd, char *buffer, int size) {
  ssize_t bytes_written = write(fd, buffer, size);
  if (bytes_written < 0) {
    perror("Error writing to serial port");
    close(fd);
    return 1;
  }
  return 0;
}

int main() {
  printf("Press Ctrl+C to quit\n");
  // ADC设备文件的目录
  const char *adc_dir = "/sys/bus/iio/devices/iio:device0";
  // 定义三个字符数组,分别用于存储三个属性文件的路径
  char in_voltage0_raw_path[256];
  char in_voltage1_raw_path[256];
  char in_voltage_scale_path[256];
  // 字符数组赋值对应ADC的三个属性文件路径
  sprintf(in_voltage0_raw_path, "%s/in_voltage0_raw", adc_dir);
  sprintf(in_voltage1_raw_path, "%s/in_voltage1_raw", adc_dir);
  sprintf(in_voltage_scale_path, "%s/in_voltage_scale", adc_dir);
  // 打开三个ADC属性文件
  FILE *scale_file = fopen(in_voltage_scale_path, "r");
  FILE *in0_raw_file = fopen(in_voltage0_raw_path, "r");
  FILE *in1_raw_file = fopen(in_voltage1_raw_path, "r");
  // UART3设备文件路径
  char serial_port[] = "/dev/ttyS3";
  // 定义变量用于存储打开UART3后的文件符
  int serial_fd;
  // 打开UART3设备文件
  serial_fd = open(serial_port, O_RDWR | O_NOCTTY);
  if (serial_fd == -1) {
    perror("Failed to open serial port");
    return 1;
  }
  // 定义struct termios结构体,用于设置UART口参数
  struct termios tty;
  memset(&tty, 0, sizeof(tty));
  // 从 serial_fd 所引用的设备中获取当前的终端属性
  if (tcgetattr(serial_fd, &tty) != 0) {
    perror("Error from tcgetattr");
    return 1;
  }
  // 设置输入和输出波特率为115200
  cfsetospeed(&tty, B115200);
  cfsetispeed(&tty, B115200);
  // 无奇偶校验、一个停止位、8 个数据位
  tty.c_cflag &= ~PARENB;
  tty.c_cflag &= ~CSTOPB;
  tty.c_cflag &= ~CSIZE;
  tty.c_cflag |= CS8;
  // 将串口属性值最终设置回文件
  if (tcsetattr(serial_fd, TCSANOW, &tty) != 0) {
    perror("Error from tcsetattr");
    return 1;
  }
  // 串口收、发缓存和ADC读数缓存
  char rx_buffer[64];
  char tx_buffer[64];
  char buffer[32];
  float scale = 1.7578125;
  while (1) {
    int bytes_read = read(serial_fd, rx_buffer, sizeof(rx_buffer));
    // 如果串口有接收
    if (bytes_read > 0) {
      rx_buffer[bytes_read] = '\0';
      // 则先读取ADC的转换因子
      fseek(scale_file, 0, SEEK_SET);
      if (scale_file) {
        fgets(buffer, sizeof(buffer), scale_file);
        scale = strtof(buffer, NULL);
      }
      if (strstr(rx_buffer, "ADC0") != NULL) {
        // 如果接收到“ADC0”则读取ADC通道0,并串3输出
        fseek(in0_raw_file, 0, SEEK_SET);
        if (in0_raw_file) {
          fgets(buffer, sizeof(buffer), in0_raw_file);
          int in0_raw_value = atoi(buffer);
          float in0_voltage = (in0_raw_value * scale) / 1000.0;
          printf("in ADC0 value: %d, volt: %.6f\n", in0_raw_value, in0_voltage);
          memset(tx_buffer, 0, 64);
          sprintf(tx_buffer, "IN0 Voltage: %.6f V\n", in0_voltage);
          if (writeBuf(serial_fd, tx_buffer, strlen(tx_buffer)) != 0) return 1;
        }
      } else if (strstr(rx_buffer, "ADC1") != NULL) {
        // 如果接收到“ADC1”则读取ADC通道1,并串3输出
        fseek(in1_raw_file, 0, SEEK_SET);
        if (in1_raw_file) {
          fgets(buffer, sizeof(buffer), in1_raw_file);
          int in1_raw_value = atoi(buffer);
          float in1_voltage = (in1_raw_value * scale) / 1000.0;
          printf("in ADC1 value: %d,  volt:%.6f\n", in1_raw_value, in1_voltage);
          memset(tx_buffer, 0, 64);
          sprintf(tx_buffer, "IN1 Voltage: %.6f V\n", in1_voltage);
          if (writeBuf(serial_fd, tx_buffer, strlen(tx_buffer)) != 0) return 1;
        }
      }
    } else {
      printf("No data received.\n");
    }

    sleep(1);
  }

  fclose(scale_file);
  fclose(in0_raw_file);
  fclose(in1_raw_file);
  close(serial_fd);
  return 0;
}

 

最新回复

他的ADC是多少位的呀?看起来好象非常准一样。   详情 回复 发表于 2024-2-15 15:44
点赞 关注(1)
 
 

回复
举报

7095

帖子

11

TA的资源

版主

沙发
 

他的ADC是多少位的呀?看起来好象非常准一样。

点评

手册说明ADC电压范围是0~1.8V,读数最大1023,应该是10位吧  详情 回复 发表于 2024-2-15 16:08
 
 
 

回复

155

帖子

1

TA的资源

一粒金砂(高级)

板凳
 
lugl4313820 发表于 2024-2-15 15:44 他的ADC是多少位的呀?看起来好象非常准一样。

手册说明ADC电压范围是0~1.8V,读数最大1023,应该是10位吧

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表