Funpack2-4 基于AVR64DD32的温度采集与恒温控制系统
在本次Funpack活动中我将核心板+扩展板的任务都实现了下(ps:在完成了加热和采集后,发现加个PID,就能实现恒温自动控制了,于是就都实现了)
标签
Funpack活动
starry-m
更新2023-04-23
729

一、项目介绍

 本项目为参加Funpack2-4活动基于AVR64DD32和搭配数字系统的输入、输出扩展板实现的的温度采集与恒温控制系统。本项目使用到的扩展板上的资源为1.44寸ST7735屏幕、NST112温度传感器、SI2302 N型MOS管、RGB LED灯以及模拟输入的三个按键。

项目任务: 任务1 - 实现一个加热和温度采集系统

 ·IO扩展板上有一处加温电阻,通过电流给电阻加热,并通过温度传感器感知板上温度的变化,将测量到的温度信息显示在LCD屏幕上,绘制一个1分钟的温升曲线。并且每隔1分钟改变加热电阻的占空比,重复温度测量和绘制的过程。

 ·板上有一处RGB彩灯,当温度超过50°C时转为红色,低于20°C时转为蓝色,正常状态下为绿色。 要求:按下按键时,截图当前的温升曲线。(注意,加热电阻满占空比开启后温度较高)

任务2 - 实现一个恒温自动控制系统

·IO扩展板上有一处加温电阻,将加热区域用物体(纸巾等)包裹起来,通过电流给电阻加热,并通过温度传感器感知板上温度的变化,测温以及在LCD屏上的温度显示。

要求:使用按键设定目标温度,并且通过程序控制加热功率,使得温度尽快尽量稳定的维持在目标温度。温度偏离设定温度±3°C彩灯变为红色。(注意,加热电阻满占空比开启后温度较高)

二、硬件电路分析

1.按键输入的处理

AVjQ1tSPblkMAAAAAElFTkSuQmCC

 如上图所示,在这块扩展板上面的按键输入并不是采用常见的数字高低电平表示,应该是因为本身的扩展板需要连接ESP32_IF,IO数量不太够。我使用到了右边的两个弹片按键和旋转编码器的按键(没使用到旋转编码器的旋转输入),这里主要就是不同按键按下时,A_Out位置的电位发生变化,为了省事我直接测量了不同状态时的电位(其实是懒的分析电路)。 按键状态如下:

按键状态 电位电压
无按键按下 3.18V
上键按下 2.35V
下键按下 1.54V
编码器按键按下 2.76V

 

2.加热电路分析

1C57QTcDDcQAAAABJRU5ErkJggg==

 如上图所示,这块扩展板上R29焊上了,R30没有焊。使用的是5V电源对板子加热,该MOS管为N型,我们在使用时基本工作在截至区或饱和区,所以,我们只需要把他当作开关就行了。当V_HEAT信号为高电平时,MOS管导通,V_HEAT信号为低电平时关闭。但我感觉这块电路有点小问题,因为我在下载程序时单片机引脚默认是高电平,会让MOS管导通。感觉可以给MOS管的栅极加个下拉电阻会更好些。

3.屏幕与温度传感器

wfNyfPMndPsIMAAAAASUVORK5CYII=

原理图中的屏幕没有做背光可调电路,应该也是出于节约引脚的考虑。

WgAAAABJRU5ErkJggg==

扩展板上使用的温度传感器NST112已经高度集成了,我们只需要通过I2C通讯协议配置模式和读取温度就行了,值得一提的是该传感器芯片还有个Alert引脚用于超过设置的温度区间进行报警,但因为本次项目不需要该功能,故没有连接。

三、软件设计

   对于本次的AVR64DD32我没有使用官方的开发环境(因为太过陌生,以后有时间再看看),我使用的是在VS code中的platformio环境ARDUINO框架。在此感谢乔楚大佬的环境搭建教程,链接为:FunPack4 AVR64DD32 使用入门

1.驱动部分

  因为在ARDUINO框架下适配了极多的驱动库,我便愉快的使用了Adafruit ST7735 and ST7789 Library。然后当我想再次找现成的NST112驱动库时。。。我没找到。好吧,既然没有,那咱就自己翻手册写吧,因为基础的I2C通信我可以直接使用,再加上它内部的寄存器极少,且操作很简单,我很快就搞定了(使用了默认的连续转换 12bit分辨率 4HZ采样率 ,为了方便操作,读取温度为正常温度100倍)。

一开始读到的温度始终为乱码,于是我用逻辑分析仪(上位机为pulse view),来查看我写入和读取是否正确,查看后发现符合预期。后尝试直接12位输出数字量正确,让我怀疑是不是软串口打印的问题,发现果然是软串口打印浮点数有问题,然后换成模拟量温度放大100倍,再打印整数,乱码问题解决。

+s72gmyNoaAAAAAElFTkSuQmCC

驱动程序如下

//NST112.h
#ifndef __NST112_H__
#define __NST112_H__

#define NST112_ADDR  0x48 //1001000
#define NST112_temperature_Register 0x00
#define NST112_config_Register 0x01
#define NST112_temp_Low_Register 0x02
#define NST112_temp_High_Register 0x03
class NST112
{
private:
    /* data */
  void    write(uint8_t _register, uint16_t _data);
  uint16_t read(uint8_t _register);
public:
    NST112(/* args */);
    ~NST112();
    void init();
    void init(uint8_t sda,uint8_t scl);
    bool getTemperature(int16_t *temp);
    bool set_temp_alert_high(int16_t *temph);
    bool set_temp_alert_low(int16_t *templ);
    bool get_temp_alert_high(int16_t *temph);
    bool get_temp_alert_low(int16_t *templ);
    uint16_t test();
};
#endif
//NST112.c
#include <Arduino.h>
#include <Wire.h>
#include "NST112.h"

NST112::NST112(/* args */)
{
}
NST112::~NST112()
{
}
void NST112::write(uint8_t _register, uint16_t _data)
{
    uint8_t data_array[2]={_data>>8,_data&0xFF};
    Wire.begin();
    Wire.beginTransmission(NST112_ADDR);
    Wire.write(_register);
    Wire.write(data_array,2);
    Wire.endTransmission();
}
uint16_t NST112::read(uint8_t _register)
{
    uint16_t data_read;
    uint8_t data_array[2];
    Wire.begin();
    Wire.beginTransmission(NST112_ADDR);
    Wire.write(_register);
    Wire.endTransmission();
    Wire.beginTransmission(NST112_ADDR);
    Wire.requestFrom(NST112_ADDR,2);
    while(Wire.available())
    {
        Wire.readBytes(data_array,2);
    }
    Wire.endTransmission();
    data_read=data_array[0]*256 +data_array[1];
    return data_read;
}
uint16_t NST112::test()
{
      uint16_t data_read;
    uint8_t data_array[2];
    // Wire.begin();
    Wire.beginTransmission(NST112_ADDR);
    Wire.write(NST112_temperature_Register);
    Wire.endTransmission();
    Wire.beginTransmission(NST112_ADDR);
    Wire.requestFrom(NST112_ADDR,2);
    while(Wire.available())
    {
        Wire.readBytes(data_array,2);
    }
    Wire.endTransmission();
    data_read=data_array[0]*256 +data_array[1];
    return data_read;  
}
void NST112::init()
{
    // Wire.begin();
    write(NST112_config_Register,0b0110000010100000); //0110_0001_1100_0000  0x61C0

}
void NST112::init(uint8_t sda,uint8_t scl)
{
    Wire.pins(sda,scl);
    write(NST112_config_Register,0x61C0); //0110_0001_1100_0000  0x61C0

}
bool NST112::getTemperature(int16_t *temp)
{
    uint16_t _data=read(NST112_temperature_Register) >>4;
    float val=0;
    if(_data&0x800)
    {
        val=(0xFFF-_data +1)*-6.25f;
    }
    else
    {
        val=6.25f*_data;
    }
    // val=0.0625f*_data;
    *temp=int16_t(val);
    return 1;
}

bool NST112::set_temp_alert_high(int16_t *temph)
{
    uint8_t temp_Set[2];
    uint16_t temp_data=0;
    int16_t temp_val=*temph;

    temp_data=temp_val / 6.25;
    temp_data <<=4;
    write(NST112_temp_High_Register,temp_data);
}
bool NST112::set_temp_alert_low(int16_t *templ)
{
    uint8_t temp_Set[2];
    uint16_t temp_data=0;
    int16_t temp_val=*templ;
    if(temp_val<0)
        temp_val= -1*temp_val;
    temp_data=temp_val / 6.25;
    temp_data <<=4;
    temp_data =0x0FFF +1-temp_data;
    write(NST112_temp_High_Register,temp_data);
}
bool NST112::get_temp_alert_high(int16_t *temph)
{
    uint16_t _data=read(NST112_temp_High_Register) >>4;
    int16_t val=0;
    if(_data&0x800)
    {
        val=(0xFFF-_data +1)*6.25;
    }
    else
    {
        val=6.25*_data;
    }
    *temph=val;
    return 1;
}
bool NST112::get_temp_alert_low(int16_t *templ)
{
    uint16_t _data=read(NST112_temp_High_Register) >>4;
    int16_t val=0;
    if(_data&0x800)
    {
        val=(0xFFF-_data +1)*-6.25;
    }
    else
    {
        val=6.25*_data;
    }
    *templ=val;
    return 1;
}


   对于按键输入的识别使用的是常见的延时20ms再判断的思路,只是把单纯的高低电平判断变成了范围(因为电阻精度的问题,肯定会有抖动的)

uint8_t key_scan(void)
{
  static uint8_t key_up = 1;
  uint16_t adc_read = 0;
  adc_read = analogRead(KEY_Analog);
  if (key_up && adc_read < 950)
  {
    delay(10);
    key_up = 0;
    adc_read = analogRead(KEY_Analog);
    if (700 < adc_read && adc_read < 760)
      return KEY_UP;
    else if (450 < adc_read && adc_read < 510)
      return KEY_DOWN;
    else if (830 < adc_read && adc_read < 910)
      return KEY_ENC;
    else
      return KEY_NONE;
  }
  else if (adc_read > 950)
    key_up = 1;

  return KEY_NONE;
}

2.IO连接与实现思路

io连接如下

外设信号 MCU引脚 Arduino引脚编号
LCD_SCL PA6 6
LCD_SDA PA4 4
LCD_RESn PC0 8
LCD_DC PC1 9
LCD_CSn PA7 7
LED_R PD2 14
LED_G PD3 15
LED_B PD6 18
V_HEAT PD1 13
A_Out PD7 19
I2C_SCL PA3 3
I2C_SDA PA2 2

其中屏幕用到了SPI,温度传感器用到I2C,MOS管控制用到了PWM,按键输入用到了ADC,以及其他的普通IO。

实现思路

1、因为任务要求绘制一个1分钟的温升曲线,所以我用了个软件定时器进行1S的定时采集温度和曲线绘制,同时还要按下按键具有截图功能,所以还有两个缓存用于波形曲线缓存(使用两个的原因是为了在59S刷新时不会丢掉之前缓存的波形)。

2、恒温控制使用的是最经典的PID控制,详细的理论不再赘述,相信随便搜个视频都比我讲的好,哈哈。 自己随手写的PID控制如下(PID的参数只是自己感觉差不多了就没调了):

uint8_t ACTC_process(double set_value, double in_value)
{
  static double actc_kp = 15.0, actc_ki = 1.0, actc_kd = 2.0;
  static double now_value = 0, last_value = 0;
  static double error_add = 0.0;
  double out_value = 0;
  now_value = in_value;
  out_value = actc_kp * (set_value - now_value) + actc_ki * error_add + actc_kd * (now_value - last_value);
  error_add += set_value - now_value;
   last_value = now_value;
  if (error_add > 80)
    error_add = 80;
  if (out_value < 0)
    out_value = 0;
  else if (out_value > 254)
    out_value = 254;
  return out_value;
}

实现代码如下:

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
// #include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <SPI.h>
#include <SoftwareSerial.h>
#include "NST112.h"
#include <Wire.h>
#include <arduino-timer.h>

// LCD
#define TFT_CS 7
#define TFT_RST 8 // Or set to -1 and connect to Arduino RESET pin
#define TFT_DC 9
#define SerialDebugging true
#define LCD_SDA 4
#define LCD_SCL 6

// RGB_LED
#define LED_R 14 // G
#define LED_G 15 // B
#define LED_B 18 // R
// 模拟按键
/*
NONE 990
UP   734  700-760
DOWM 482  450-510
ENC  860  830-910
*/
#define KEY_Analog 19
// N MOS PWM
#define Heat_PWM 13
// I2C temperature
#define NST_SCL 3
#define NST_SDA 2

#define KEY_NONE 0
#define KEY_UP 1
#define KEY_DOWN 2
#define KEY_ENC 3
uint8_t key_scan(void);

NST112 m_nst112;
SoftwareSerial softSerial1(PIN_PD5, PIN_PD4);
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, LCD_SDA, LCD_SCL, TFT_RST);

int16_t nst112_temp;
uint8_t pwm_duty = 0;
uint8_t sys_mode = 0;
/*
0-  chang  pwm duty
1=  up :save graphics dowm:show
2=  auto constan temp control
*/
uint8_t ACTC_temp = 50;
uint8_t ACTC_pwm_duty = 0;
void Draw_string(int16_t x, int16_t y, uint8_t *strings, uint8_t size, uint16_t color);
void GUI_Init(void);
void rect_clear(void);
void Draw_Graph(uint8_t index);
void Screen_Shot_Show(void);
void Temp_to_Led(uint8_t temperature);
void ACTC_show(void);
uint8_t ACTC_process(double set_value, double in_value);

auto timer = timer_create_default(); // create a timer with default settings
uint8_t temp_graph_buff[60];
uint8_t temp_graph_buff_1[60];

uint8_t temp_graph_index = 0;
uint8_t screen_shot_en = 0;
bool period_1s_event(void *)
{
  static uint8_t time_counter = 0;
  if (sys_mode == 0)
  {
    m_nst112.getTemperature(&nst112_temp);
    softSerial1.printf("%d:Temperature=%d.%d 'c\n", time_counter, nst112_temp / 100, nst112_temp % 100);
    if (time_counter == 1)
    {
      rect_clear();
    }
    Draw_Graph(time_counter);
    if (time_counter >= 59)
    {
      if (pwm_duty < 200)
        pwm_duty += 100;
      else
        pwm_duty = 0;
      analogWrite(Heat_PWM, pwm_duty);
      time_counter = 0;
    }
    else
      time_counter++;
    Temp_to_Led(nst112_temp / 100);
    // digitalWrite(LED_G, !digitalRead(LED_G)); // toggle the LED
  }
  else if (sys_mode == 1)
  {
    time_counter = 0;
    analogWrite(Heat_PWM, 0);
    digitalWrite(LED_R, HIGH);
    digitalWrite(LED_G, HIGH);
    digitalWrite(LED_B, HIGH);
  }
  else if (sys_mode == 2)
  {
    ACTC_show();
  }

  // softSerial1.printf("ADC READ: %d\n", analogRead(KEY_Analog));
  return true; // repeat? true
}

void setup(void)
{
  softSerial1.begin(9600);
  pinMode(KEY_Analog, INPUT);
  pinMode(Heat_PWM, OUTPUT);
  analogWrite(Heat_PWM, 0);
  pinMode(LED_R, OUTPUT);
  pinMode(LED_G, OUTPUT);
  pinMode(LED_B, OUTPUT);

  timer.every(1000, period_1s_event);
  m_nst112.init();

  tft.initR(INITR_144GREENTAB); // Init ST7735R chip, green tab
  GUI_Init();
}
void loop()
{
  timer.tick(); // tick the timer
  uint8_t key_state = key_scan();
  switch (key_state)
  {
  case KEY_UP:
    if (sys_mode == 0)
    {
      if (pwm_duty <= 110)
        pwm_duty += 10;
      else
        pwm_duty = 0;
      analogWrite(Heat_PWM, pwm_duty);
      softSerial1.printf("pwm_duty=%d 'c\n", pwm_duty);
    }
    else if (sys_mode == 1)
    {
      Screen_Shot_Show();
    }
    else if (sys_mode == 2)
    {
      ACTC_temp += 10;
    }
    break;
  case KEY_DOWN:
    if (sys_mode == 0)
    {

      screen_shot_en = 1;
    }
    else if (sys_mode == 1)
    {
    }
    else if (sys_mode == 2)
    {
      // PID_Setpoint = ACTC_temp;
    }
    break;

  case KEY_ENC:
    sys_mode = sys_mode < 2 ? sys_mode + 1 : 0;
    softSerial1.printf("KEY ENC\n");
  default:
    break;
  }
  if (sys_mode == 2)
  {
    m_nst112.getTemperature(&nst112_temp);
    ACTC_pwm_duty = ACTC_process(ACTC_temp, nst112_temp / 100);
    analogWrite(Heat_PWM, ACTC_pwm_duty);
    softSerial1.println(ACTC_pwm_duty);
    delay(100);
  }
}

uint8_t key_scan(void)
{
  static uint8_t key_up = 1;
  uint16_t adc_read = 0;
  adc_read = analogRead(KEY_Analog);
  if (key_up && adc_read < 950)
  {
    delay(10);
    key_up = 0;
    adc_read = analogRead(KEY_Analog);
    if (700 < adc_read && adc_read < 760)
      return KEY_UP;
    else if (450 < adc_read && adc_read < 510)
      return KEY_DOWN;
    else if (830 < adc_read && adc_read < 910)
      return KEY_ENC;
    else
      return KEY_NONE;
  }
  else if (adc_read > 950)
    key_up = 1;

  return KEY_NONE;
}
void Draw_string(int16_t x, int16_t y, const char *strings, uint8_t size, uint16_t color)
{
  uint8_t temp = 0;
  while (strings[temp] != '\0')
  {
    tft.drawChar(x + temp * size * 6, y, strings[temp], color, ST77XX_WHITE, size);
    temp++;
  }
}
void GUI_Init(void)
{
  tft.fillScreen(ST77XX_WHITE);
  tft.drawRect(1, 10, 60, 100, ST77XX_BLACK);
  tft.drawFastHLine(1, 60, 10, ST77XX_BLACK);
  tft.drawFastVLine(31, 100, 10, ST77XX_BLACK);
  Draw_string(61, 10, "temp:", 2, ST77XX_BLUE);
  Draw_string(61, 50, "duty:", 2, ST77XX_BLUE);
  Draw_string(0, 0, "100`C:", 1, ST77XX_BLUE);
  Draw_string(0, 110, "0", 1, ST77XX_BLUE);
  Draw_string(61, 110, "60s", 1, ST77XX_BLUE);
  Draw_string(100, 80, "/255", 1, ST77XX_BLUE);
}
void rect_clear(void)
{
  tft.fillRoundRect(1, 10, 60, 100, 10, ST77XX_WHITE);
  tft.drawRect(1, 10, 60, 100, ST77XX_BLACK);
  tft.drawFastHLine(1, 60, 10, ST77XX_BLACK);
  tft.drawFastVLine(31, 100, 10, ST77XX_BLACK);
}
void Draw_Graph(uint8_t index)
{
  static char show_buff[20];
  static uint8_t last_temp = 0, now_temp = 0;
  sprintf(show_buff, "%d.%d", nst112_temp / 100, nst112_temp % 100);
  Draw_string(61, 30, show_buff, 2, ST77XX_BLUE);
  sprintf(show_buff, "%3d", pwm_duty);
  Draw_string(61, 70, show_buff, 2, ST77XX_BLUE);
  now_temp = nst112_temp / 100;
  if (index)
    tft.drawLine(index, 110 - last_temp, 1 + index, 110 - now_temp, ST77XX_RED);
  temp_graph_buff[index] = now_temp;
  if (screen_shot_en && temp_graph_index + 1 == index)
  {
    screen_shot_en = 0;

    tft.fillRoundRect(10, 120, 80, 10, 10, ST77XX_WHITE);
  }
  if (screen_shot_en)
  {
    Draw_string(10, 120, "screenshoted!", 1, ST77XX_ORANGE);
    memcpy(temp_graph_buff_1, temp_graph_buff, 60);
  }
  temp_graph_index = screen_shot_en ? index : temp_graph_index;
  sprintf(show_buff, "%2d", index);
  Draw_string(90, 100, show_buff, 2, ST77XX_ORANGE);
  last_temp = now_temp;
}
void Screen_Shot_Show(void)
{
  tft.fillRoundRect(1, 10, 60, 100, 10, ST77XX_WHITE);
  tft.drawRect(1, 10, 60, 100, ST77XX_BLACK);
  tft.drawFastHLine(1, 60, 10, ST77XX_BLACK);
  tft.drawFastVLine(31, 100, 10, ST77XX_BLACK);
  for (uint16_t i = 0; i < temp_graph_index; i++)
  {
    tft.drawLine(i + 1, 110 - temp_graph_buff_1[i], 2 + i, 110 - temp_graph_buff_1[i + 1], ST77XX_GREEN);
  }
}

void Temp_to_Led(uint8_t temperature)
{
  if (temperature >= 50)
  {
    digitalWrite(LED_R, LOW);
    digitalWrite(LED_G, HIGH);
    digitalWrite(LED_B, HIGH);
  }
  else if (temperature < 20)
  {
    digitalWrite(LED_R, HIGH);
    digitalWrite(LED_G, HIGH);
    digitalWrite(LED_B, LOW);
  }
  else
  {
    digitalWrite(LED_R, HIGH);
    digitalWrite(LED_G, LOW);
    digitalWrite(LED_B, HIGH);
  }
}
void ACTC_show(void)
{
  static char show_buff[20];
  static uint8_t index = 0, last_temp = 0, now_temp = 0;
  // static uint8_t  temp_graph_flag=1;
  sprintf(show_buff, "%d.%d", nst112_temp / 100, nst112_temp % 100);
  Draw_string(61, 30, show_buff, 2, ST77XX_BLUE);
  sprintf(show_buff, "%3d", ACTC_pwm_duty);
  Draw_string(61, 70, show_buff, 2, ST77XX_BLUE);

  now_temp = nst112_temp / 100;
  if (index)
    tft.drawLine(index, 110 - last_temp, 1 + index, 110 - now_temp, ST77XX_RED);
  sprintf(show_buff, "set-temp");
  Draw_string(70, 100, show_buff, 1, ST77XX_ORANGE);
  sprintf(show_buff, "%2d", ACTC_temp);
  Draw_string(80, 110, show_buff, 2, ST77XX_ORANGE);
  if (index == 59)
  {
    rect_clear();
  }
  if (index < 59)
    index++;
  else
    index = 0;
  if (abs(now_temp - ACTC_temp) >= 3)
  {
    digitalWrite(LED_R, LOW);
    digitalWrite(LED_G, HIGH);
    digitalWrite(LED_B, HIGH);
  }
  else
  {
    digitalWrite(LED_R, HIGH);
    digitalWrite(LED_G, HIGH);
    digitalWrite(LED_B, HIGH);
  }
  last_temp = now_temp;
}
uint8_t ACTC_process(double set_value, double in_value)
{
  static double actc_kp = 15.0, actc_ki = 1.0, actc_kd = 2.0;
  static double now_value = 0, last_value = 0;
  static double error_add = 0.0;
  double out_value = 0;
  now_value = in_value;
  out_value = actc_kp * (set_value - now_value) + actc_ki * error_add + actc_kd * (now_value - last_value);
  error_add += set_value - now_value;
  last_value = now_value;
  if (error_add > 80)
    error_add = 80;
  if (out_value < 0)
    out_value = 0;
  else if (out_value > 254)
    out_value = 254;
  return out_value;
}

四、效果展示

1、温度采集与绘制

FidHjlzy2gm5Y6Zmhmrk7U150Oje

图4-1关断MOS管不加热时

Fv1ytfTz189rq_C0gTri_ohQdNLR

图4-2开启MOS管占空比为100/255时

在按下按键时,会显示”screenshooted!“ 1秒的时间,表示已截取当前曲线。

FslwzPE0WJ3k6CgVK_C5g3Q3HGgD

图4-2截屏曲线读取

按下按键读取截屏曲线,且换用绿色绘制。

2、恒温控制

FtCuf89nKBj37JsgOonkcUG-QPxL

图4-3升温接近设定温度时,未达到正负3度内亮红灯

FlgKPdn-MczaBNL4zKbPFggBBNeP

图4-3升温接近设定温度时,达到正负3度内红灯熄灭

五、心得体会

之前忙着毕业设计和论文这些杂七杂八的事,直到Lucia老师在群里提醒,才想起来还有这个活动没做。匆匆忙忙搞了几天,总算搞完了,但因为时间仓促,都没去好好的了解这款8位MCU,得长个教训。不然用了这个单片机,连他的特点和不足都说不出来多少也太丢脸了。

总的来说,电子森林举办Funpack这种活动让我们开阔了不少视野,不再只是盯着ST的32系列和TI这些我们耳熟能祥的单片机看,知道了,原来还有这么多的单片机,可玩性还可以。

上次我参加的寒假训练营也是第一次知道了国外的FPGA除了ALTERA和XILINX还有LATTICE这种还不错的。很感谢电子森林让我开阔了视野,参加了有趣的活动。

附件下载
AVR64DD32_TEST.zip
platformio工程源码
团队介绍
一名即将毕业的本科生
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号