从STC10/STC11系列开始出现内置RC震荡源(内置时脉, 宏晶称之为IRC). 最初的三代STC10/STC11/STC12, 在手册上的介绍都是"由于使用内部时钟源误差较大, … 有串行通信的情况下不建议使用内部R/C时钟源". 到STC15这一代之后, 精度提升明显, 开始出现了默认不使用外置晶振的MCU型号. 从STC8开始对IRC进行了更多的改进, 从STC8A/8F的一个频段, 到STC8G/STC8H的两个频段, 到STC8A8K64D4的4个频段, 从CODE预置, 到XDATA只读预置, 可用性也在不断提升.
这里说一下STC8系列的IRC设置.
只有一个IRC频段, 频率范围在16-27MHz, 通过两个寄存器调节内部时脉RTRIM, LIRTIM, 通过 CLKDIV 分频后作为系统时钟SYSCLK
code
, 通过*(char code *)0xfdf3
这样的形式访问
idata
, 通过*(unsigned char idata *)0xFB
这样的形式访问
如果在Linux下开发, 不能使用STC-ISP, 则上述的方式都无效, 要通过辅助手段标定
实现思路
STC8G的内置振荡源频段有两个: 20MHz和33MHz, 可以从20MHz覆盖到36.864MHz,
因为存在多个频段, 多了两个寄存器用于频段的切换
在设置频率时, 会涉及到这4个寄存器 IRCBAND, VRTRIM, IRTRIM, LIRTRIM, 都会对频率有影响, 调节程度从左到右递减
一个例子
ADDR: 0x1FE9
VER1 VER2
VRTRIM 35M 40M: 20
VRTRIM 20M 24M: 1F
ITRIM --- 48M: FF
ITRIM --- 44.2368M: D0
ITRIM 36.864M 40M: A3
ITRIM 35M 36.864M: 88
ITRIM 33.1776M 35M: 6F
ITRIM 30M 33.1776M: 43
ITRIM 27M 30M: 1A
ITRIM 20M 27M: 63
ITRIM 24M 24M: BA
ITRIM 22.1184M 22.1184M: 90
32kHz PD FreQ: 8DCC
1.19Vref: 04A9
MCUID: F7 A4 C4 0D 11 E0 EE
Current VRTRIM:20, IRTRIM:A3, LIRTRIM:03
STC8H要区分两个不同的系列
内置振荡源频段有两个, 20MHz和35MHz, STC-ISP预设的数值, 右边一列是芯片实测数据
ADDR: 0x1FE9
VER1 VER2
VRTRIM 35M 40M: 1F
VRTRIM 20M 24M: 1E
ITRIM --- 48M: FF
ITRIM --- 44.2368M: E4
ITRIM 36.864M 40M: B5
ITRIM 35M 36.864M: 9A
ITRIM 33.1776M 35M: 7E
ITRIM 30M 33.1776M: 51
ITRIM 27M 30M: 26
ITRIM 20M 27M: 73
ITRIM 24M 24M: D0
ITRIM 22.1184M 22.1184M: A4
32kHz PD FreQ: 8A48
1.19Vref: 04AA
MCUID: F7 34 C5 68 00 11 22
Current VRTRIM:1F, IRTRIM:B7, LIRTRIM:03
这里是和手册不一致的地方, 对于STC8H3K32S2, F/W version: 7.4.1U, 实际上有4个频段, 也就是IRCBAND取值从 0x00 - 0x03, 24MHz和40MHz这两个属于0x02和0x03, 预设的寄存器要提前两个字节, 从0x7FE7开始读, 这样才是正确的, 研究这个问题耽误了半天时间. 手册不一致, 真是坑爹.
ADDR: 0x7FE7
VER1 VER2
VRTRIM 40M: 19
VRTRIM 24M: 1C
VRTRIM 35M ??M: 20
VRTRIM 20M ??M: 1E
ITRIM --- 45M: 7C
ITRIM --- 40M: 47
ITRIM 36.864M: 2D
ITRIM 35M: 12
ITRIM 33.1776M: FF
ITRIM 30M: D2
ITRIM 27M: 98
ITRIM 20M: 1A
ITRIM 24M: 64
ITRIM 22.1184M: 41
32kHz PD FreQ: 8D04
1.19Vref: 04A3
MCUID: F7 4A C5 26 03 11 22
Current IRCBAND:03, VRTRIM:19, IRTRIM:2D, LIRTRIM:00
在设置时, 如果选择了高频段(35MHz或40MHz), ITRIM不能设置得太高, 如果设置得太高(超过0xE0), 会导致芯片无法启动
内置RC振荡的频段增加到了4个: 6M, 10M, 27M, 44M,
STC8A8K64D4是个比较特殊的型号, 你可以看成和STC8H8K功能一样, 只是引脚布局做成了和STC8A8K兼容的布局. 可以看成是为了让之前使用STC8A8K的客户能不改PCB升级的一个STC8H版本.
使用方式和4个IRC频段的STC8H一致, 也是通过IRCBAND, VRTRIM, IRTRIM, LIRTRIM这4个寄存器调节频率.
记录一下避免其他人踩坑
对于在Linux下开发的, 建议还是要在Windows下通过STC-ISP烧录一次, 这样是最方便快捷的方式得到芯片的预设标定值.
如果在程序开始时调节了 CLKDIV, 至少需要等待 0x2FFFF 个时钟周期后再进行其它操作, 否则极容易导致后续的UART和Timer设置出现莫名其妙的错误, 这是今天排查了一整天才填的坑.
/**
* STC8H Clock:
* MCKSEL ||===> MCLKODIV ==> MCLKO_S => P1.6/P5.4
* 00 Internal IRC | ||
* 01 External OSC |==> CLKDIV ==> SYSCLK
* 10 External 32KHz |
* 11 Internal 32KHz |
*/
#define SYS_SetFOSC(__IRCBAND__, __VRTRIM__, __IRTRIM__, __LIRTRIM__) do { \
IRCBAND = ((__IRCBAND__) & 0x03); \
VRTRIM = (__VRTRIM__); \
IRTRIM = (__IRTRIM__); \
LIRTRIM = ((__LIRTRIM__) & 0x03); \
} while(0)
/**
* Change system clock
* - invoke this in the beginning of code
* - don't invoke this if the target frequency is already set by STC-ISP
*/
void SYS_SetClock(void)
{
uint16_t i = 0; uint8_t j = 5;
P_SW2 = 0x80;
if (CLKDIV != (__CONF_CLKDIV))
{
CLKDIV = (__CONF_CLKDIV);
do { // Wait a while after clock changed, or it may block the main process
while (--i);
} while (--j);
}
P_SW2 = 0x00;
clkdiv = (__CONF_CLKDIV == 0)? 1 : __CONF_CLKDIV;
SYS_SetFOSC(__CONF_IRCBAND, __CONF_VRTRIM, __CONF_IRTRIM, __CONF_LIRTRIM);
while (--i); // Wait
}
INTERRUPT(tm0isr, 1)
{
uint8_t i, j;
counter++;
if (counter == 1000)
{
i = 0;
counter = 0;
UTIL_PrintString("ADDR: 0x");
UTIL_PrintHex(__CID_ADDR >> 8);
UTIL_PrintHex(__CID_ADDR & 0xFF);
UTIL_PrintString("\r\n");
UTIL_PrintString(" VER1 VER2\r\n");
UTIL_PrintString("VRTRIM 40M: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("VRTRIM 24M: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("VRTRIM 35M ??M: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("VRTRIM 20M ??M: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("ITRIM --- 45M: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("ITRIM --- 40M: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("ITRIM 36.864M: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("ITRIM 35M: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("ITRIM 33.1776M: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("ITRIM 30M: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("ITRIM 27M: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("ITRIM 20M: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("ITRIM 24M: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("ITRIM 22.1184M: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("32kHz PD FreQ: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("1.19Vref: ");
UTIL_PrintHex(readCode(i++));
UTIL_PrintHex(readCode(i++));
UTIL_PrintString("\r\n");
UTIL_PrintString("\r\n");
UTIL_PrintString("MCUID: ");
for (j = 0; j < 7; j++)
{
UTIL_PrintHex(readCode(i+j));
UTIL_PrintChar(' ');
}
UTIL_PrintString("\r\n");
UTIL_PrintString("Current IRCBAND:");
UTIL_PrintHex(IRCBAND);
UTIL_PrintString(", VRTRIM:");
UTIL_PrintHex(VRTRIM);
UTIL_PrintString(", IRTRIM:");
UTIL_PrintHex(IRTRIM);
UTIL_PrintString(", LIRTRIM:");
UTIL_PrintHex(LIRTRIM);
UTIL_PrintString("\r\n\r\n");
}
}