About

之前学单片机的时候用的是 STC89C52RC, 并且还是那种非常完善的开发板套件. 手里一堆奇奇怪怪的教程, 所以用起来还算是无脑过, 并且因为写得少, 没有啥大坑.

现在做项目为了省钱和小型化要换一块芯片: STC8G1K08A, 在这里记录一下使用中遇到的一些小问题和解决方案.

选型

本来应该为了好测试, 应该买 DIP 直插的, 但是卖完了(?) 并且贵一半, 所以最后选择用转接板和 SOP8 型号的.

简单的准备工作

第一次尝试

最小电路

/_img/STC8G1K08A/minimum-system.png

按照手册上的说明确实可以这样做. 其中电容用于稳定电压输入, 理论上换成任意一个稳压结构应该都可以吧. TX 和 RX 为下载器上对应的引脚 (即芯片的 TX 对应下载器的 RX).

下载器 CH340C

虽然一个 CH340C 芯片只要不到 1 元钱, 但是成品的下载器至少 9 元起步… 欸, 一个省钱小窍门: 不买立省 100%. 只要从旧的开发板上拆下来, 甚至连拆都不用拆, 直接看原理图, 把对应的引脚引出来就好了.

/_img/STC8G1K08A/downloader.jpeg

(请忽略这个混乱的连线)

STC8PROG

之前使用的 stcgal 貌似没法识别芯片?

> stcgal -P /dev/tty.usbserial-1111 -P stc8
Waiting for MCU, please cycle power: done
WARNING: Unknown model F794!
Target model:
  Name: UNKNOWN
  Magic: F794
  Code flash: 62.0 KB
  EEPROM flash: 0.0 KB
...

所以使用的是 stc8prog 进行程序烧写.

> stc8prog -p /dev/tty.usbserial-1110
Opening port /dev/tty.usbserial-1110: done
Waiting for MCU, please cycle power: detected
MCU type: STC8G1K08A-8PIN
Protocol: STC8G/8H
F/W version: 7.3.13U
IRC frequency(Hz): unadjusted
Switching to 115200 baud, chip: set, host: set, ping: succ

烧写方法:

> stc8prog -p /dev/tty.usbserial-1110 -e -f prog.ihx

开发环境

  • 哇, 妙极. 发现 STC8PROG 作者 Github 上还有一个仓库: FwLib_STC8, 那么为了快速解决问题, 直接引用仓库. (等以后有时间了再满满琢磨官方文档里面的说明).
  • 实际上也可以到 stcai.com 上去下载库函数, 但是是对 Keil 的语法, 没法直接用到 sdcc 上面.
  • 尝试过 PlatformIO, 但是貌似并不是很行, 并且感觉不是很会用, 所以最终决定使用 Makefile 的方式来进行编译和下载.

Makefile 看得我头昏眼花, 这真是一点也不注重可读性和可维护性啊…

核心的一个部分如下:

$(BDIR)/%.rel: $(TOP)/%.c
        @printf "  CC $<\n"
        @mkdir -p $(dir $@)
        $(Q)$(CC) $< -c $(CC_CFLAGS) $(USER_INCFLAGS) $(LIB_INCFLAGS) -o $@

把最后一行展开来可能就是:

sdcc source.c -c ... -o source.rel

该部分的作用就是在对多个文件编译前先解决依赖文件的编译, 然后对于最后一个文件的编译就变成了:

sdcc source.rel requirement1.rel requirement2.rel ...

编译中使用了一个 AR, 对应的是 sdar 指令来构建一个库.

这个时候就很后悔没有去听类似的课 (不过话说真的存在这样的课程吗?)

emmm, 不如用 Rakefile 重写一遍…

重写完了. 至少比原来的好读一些. 并且也可以烧写了. 等以后闲下来再好好修改一下, 这里放出核心的代码:

Rakefile 的一个简单代码
require 'fileutils'

RAKE_DBG = false

PROJECT_NAME = 'stc8'
MCU_IRAM = 256
MCU_XRAM = 1024
MCU_CODE_SIZE = 8192

USER_CSOURCES = 'src'
USER_INCLUDES = 'include'
USER_INCFLAGS = ["-I#{USER_INCLUDES}"]

LIB_DIR  = 'lib'
LIB_LIST = Dir.children(LIB_DIR).select { |dir| File.directory? "#{LIB_DIR}/#{dir}" }
LIB_INCFLAGS = LIB_LIST.map { |lib| "-I#{LIB_DIR}/#{lib}/include" }

LIB_FLAGS = ["__CONF_FOSC=11059200UL",
             "__CONF_MCU_MODEL=MCU_MODEL_STC8G1K08",
             "__CONF_CLKDIV=0x00",
             "__CONF_IRCBAND=0x01",
             "__CONF_VRTRIM=0x1F",
             "__CONF_IRTRIM=0xB5",
             "__CONF_LIRTRIM=0x00"]

BUILD_PATH = 'build'

CC = 'sdcc'
AR = 'sdar'
SDRANLIB   = 'sdranlib'
PACKIHX    = 'packihx'
ARCH_FLAGS = "-mmcs51"
OPT        = "--opt-code-size"
CSTD       = "--std-sdcc99"
CC_CFLAGS  = [ARCH_FLAGS, OPT, CSTD] + LIB_FLAGS.map { |flag| "-D#{flag}" }
LD_CFLAGS  = [ARCH_FLAGS, OPT,
              "--iram-size #{MCU_IRAM}",
              "--xram-size #{MCU_XRAM}",
              "--code-size #{MCU_CODE_SIZE}",
              "--out-fmt-ihx"]
TGT_FLAGS = ["-rcs"]

def shell(*cmd)
  print cmd.join(" "), "\n" if RAKE_DBG
  system *cmd
end

def dir?(path, &block)
  yield path if File.directory? path
end

def file?(dir, ext, &block)
  Dir.each_child(dir) { |f| yield f if File.extname(f) == ext } if File.exist? dir
end

def extr(f, ext)
  f.gsub(File.extname(f), ext)
end

def build_file(source, target, custom=[])
  puts "Compile #{source}"
  shell CC, "-c", source, *CC_CFLAGS, *custom, *LIB_INCFLAGS, "-o", target
end

def build_lib(name, path)
  lib_file = []
  lib_path = "#{BUILD_PATH}/#{name}"

  FileUtils.mkdir_p(lib_path)

  file?("#{path}/src", ".c") { |f|
    build_file("#{path}/src/#{f}", "#{lib_path}/#{extr f, '.rel'}")
    lib_file << "#{lib_path}/#{extr f, '.rel'}"
  }

  lib_file_path = "#{BUILD_PATH}/lib/#{name}.lib"
  FileUtils.mkdir_p("#{BUILD_PATH}/lib")

  puts "Creating static lib #{name}"
  shell AR, *TGT_FLAGS, lib_file_path, *lib_file
  shell SDRANLIB, lib_file_path
end

desc "Libraries should be stored at lib dir."
task :build_lib do
  Dir.each_child(LIB_DIR) do |dir|
    dir?("#{LIB_DIR}/#{dir}") { |path| build_lib(dir, path) }
  end
end

desc "User source file should be stored in project root or src dir."
task :build_user do
  puts "Build User Files:"
  file?('.', '.c') { |f| build_file(f, "#{BUILD_PATH}/#{extr f, '.rel'}", USER_INCFLAGS) }
  file?('src', '.c') { |f| build_file("src/#{f}", "#{BUILD_PATH}/#{extr f, '.rel'}", USER_INCFLAGS) }
end

desc "Build all the project and output PROJECT_NAME.hex under BUILD_PATH."
task :build_project => [:build_lib, :build_user] do
  lib_files = []; file?("#{BUILD_PATH}/lib", '.lib') { |f| lib_files << "#{BUILD_PATH}/lib/#{f}" }
  usr_files = []; file?("#{BUILD_PATH}", '.rel') { |f| usr_files << "#{BUILD_PATH}/#{f}" }
  target    = "#{BUILD_PATH}/#{PROJECT_NAME}.hex"
  shell CC, *LD_CFLAGS, *usr_files, *lib_files, "-o", target
end

注: 实际上最后发现了一个坑爹的事实, 就是 stc8prog 虽然能识别, 但是并不能修改芯片运行的频率, 就会导致没法串口波特率没法设置.

这貌似就会导致串口读写有问题… 诶, 所以最后还是选择安装一个 Windows 虚拟机, 然后使用 STC-ISP 程序来进行下载.

哦, 问题解决了, stcgal 在更新之后虽然也能识别了, 只要在写入的时候刷入运行的 IRC 参数即可:

> stcgal -P stc8g -p /dev/tty.usbserial-1110 build/stc8.hex -t 11059

不过另外一个比较奇怪的问题就是需要设置工作模式为 Timer1, 使用 Timer2 的时候没法在我的电脑上收到数据, 但是 STC-ISP 程序却可以, 总之就是怪极了.

不管了, 能跑就好.

一些总结和总体思路的简单介绍

(之所以折叠是因为可能并不是很正确)

使用 sdcc 处理这些的一个简单流程图如下:

digraph {
  bgcolor=transparent;
  node [shape=rect];
  "Compile\nLibaries" -> "Compile\nUser Scripts"
  -> "Pack\nihx to hex" -> "Flash";
}

大概就这样先吧

正如这篇文章被归类的一样, 我将其归为 iGEM 一类, 这是因为这块芯片是我为了给 iGEM 硬件组做传感器单元做的一个尝试. 最终的效果也还行吧.

之后有空了就更新一下这个方面的东西.