STC8A8K64S4A12内部时钟的IRTRIM和LIRTRIM简单标定
STC8A8K64S4A12因为没有固化的频率调节值, 要么在STC-ISP烧录时设置写入, 要么通过idata高地址读取, 这对于Linux下的SDCC用户就非常不方便, 既不能用STC-ISP, 写入SDCC编译后的程序也无法在idata区读取对应的值.
那么对于Linux下的SDCC用户, 如何去确定这个频率调节值? 一个办法是通过逻辑分析仪去标定, 但是如果没有逻辑分析仪呢? 如果对频率要求不严格的话, 可以直接通过代码输出串口数据去标定.
因为串口波特率与系统时钟是关联的, 假定当前系统时钟频率固定, 那么对应一个给定的波特率例如9600, 对应的寄存器值是固定的, 如果芯片按这个值运行, 只有当系统时钟频率与预设的值接近(误差5%内), 上位机才能解码出正确的输出, 其它情况看到的都是乱码. 根据这个特性, 如果在代码中不断调节频率, 同时输出当时的IRTRIM和LIRTRIM值, 根据乱码和正常接收的情况, 就能判断出对应此频率的IRTRIM和LIRTRIM值.
编译这个程序后写入STC8A8K64S4A12, 使用USB2TTL连接串口1, 波特率9600, 观察输出的字符串.
当实际频率接近预设的频率时, 能观察到正常的输出. 取非乱码区间的中间点对应的值, 就可以作为此频率对应的IRTRIM和LIRTRIM值.
代码已经添加了对应的宏处理, 兼容SDCC和Keil C51编译
/*****************************************************************************/
/**
* \file itrim_detect.c
* \brief 使用固定波特率串口输出标定STC8A8K64S4A12各频率的ITRIM
* \version v0.1
******************************************************************************/
/*****************************************************************************/
/**
* \brief 自适应SDCC和Keil C51的宏处理
******************************************************************************/
/** SDCC - Small Device C Compiler
* http://sdcc.sf.net
*/
#if defined (SDCC) || defined (__SDCC)
# define SBIT(name, addr, bit) __sbit __at(addr+bit) name
# define SFR(name, addr) __sfr __at(addr) name
# define SFRX(name, addr) __xdata volatile unsigned char __at(addr) name
#define NOP() __asm NOP __endasm
/** Keil C51
* http://www.keil.com
*/
#elif defined __CX51__
# define SBIT(name, addr, bit) sbit name = addr^bit
# define SFR(name, addr) sfr name = addr
# define SFRX(name, addr) volatile unsigned char xdata name _at_ addr
extern void _nop_ (void);
#define NOP() _nop_()
/** default
* unrecognized compiler
*/
#else
# warning unrecognized compiler
# define SBIT(name, addr, bit) volatile bool name
# define SFR(name, addr) volatile unsigned char name
# define SFRX(name, addr) volatile unsigned char name
#endif
/*****************************************************************************/
/**
* \brief 代码中涉及的寄存器
******************************************************************************/
SBIT(TI, 0x98, 1);
SFR(PCON, 0x87);
SFR(AUXR, 0x8E);
SFR(SCON, 0x98);
SFR(SBUF, 0x99);
SFR(LIRTRIM, 0x9E);
SFR(IRTRIM, 0x9F);
SFR(P_SW2, 0xBA);
SFR(T2H, 0xD6);
SFR(T2L, 0xD7);
SFRX(CLKDIV, 0xfe01);
SFRX(IRC24MCR, 0xfe02);
static const char hexTable[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
/*****************************************************************************/
/**
* \brief 时钟和串口1初始化
******************************************************************************/
void clock_init()
{
// [ BAH,0,0x00]: 外设端口切换控制寄存器2,串口2/3/4,I2C,比较器
P_SW2 = 0x80;
// [FE01H,1,0x00]: 时钟分频寄存器,ISP可能写入预设值
CLKDIV = 0x00;
// [ 9EH,0,0x00]: IRC频率微调寄存器, ISP可能写入预设值
LIRTRIM = 0x00;
// [ BAH,0,0x00]: 外设端口切换控制寄存器2,串口2/3/4,I2C,比较器
P_SW2 = 0x00;
// [ 87H,0,0x30]: 电源控制寄存器
PCON = 0xB0;
// [ 98H,0,0x00]: 串口1控制寄存器
SCON = 0x50;
// [ 8EH,0,0x01]: 辅助寄存器
AUXR = 0x15;
}
/*****************************************************************************/
/**
* \brief 不同频率对应的串口初始化程序
******************************************************************************/
void uart_init_18m_9600()
{
// [ D6H,0,0x00]: 定时器2高字节
T2H = 0xFE;
// [ D7H,0,0x00]: 定时器2低字节
T2L = 0x2B;
}
void uart_init_22m1184_9600()
{
// [ D6H,0,0x00]: 定时器2高字节
T2H = 0xFD;
// [ D7H,0,0x00]: 定时器2低字节
T2L = 0xC0;
}
void uart_init_24m_9600()
{
// [ D6H,0,0x00]: 定时器2高字节
T2H = 0xFD;
// [ D7H,0,0x00]: 定时器2低字节
T2L = 0x8F;
}
void uart_init_28m_9600()
{
// [ D6H,0,0x00]: 定时器2高字节
T2H = 0xFD;
// [ D7H,0,0x00]: 定时器2低字节
T2L = 0x26;
}
void uart_init_32m_9600()
{
// [ D6H,0,0x00]: 定时器2高字节
T2H = 0xFC;
// [ D7H,0,0x00]: 定时器2低字节
T2L = 0xBE;
}
void uart_init_33m1776_9600()
{
// [ D6H,0,0x00]: 定时器2高字节
T2H = 0xFC;
// [ D7H,0,0x00]: 定时器2低字节
T2L = 0xA0;
}
/*****************************************************************************/
/**
* \brief 通过修改IRTRIM和LIRTRIM调节内部时钟频率
******************************************************************************/
void trim_freq(unsigned char trim, unsigned char litrim)
{
IRTRIM = trim;
LIRTRIM = litrim;
while(!(IRC24MCR & 0x01));
}
void PrintChar(unsigned char dat)
{
SBUF = dat;
while(!TI);
TI = 0;
}
void PrintHex(unsigned char hex)
{
PrintChar(hexTable[hex >> 4]);
PrintChar(hexTable[hex & 0xF]);
}
void PrintString(unsigned char *str)
{
while (*str != '\0')
{
SBUF = *str;
while(!TI);
TI = 0; /* clear */
str++;
}
}
void Delay100ms()
{
unsigned char j, k;
j = 100;
k = 228;
do
{
while (--k);
} while (--j);
}
void DetectItrim(unsigned char *str)
{
unsigned char i, j;
do
{
j = 3;
do
{
trim_freq(i, j);
PrintHex(IRTRIM);
PrintChar(0x20);
PrintHex(LIRTRIM);
PrintChar(0x20);
PrintString(str);
Delay100ms();
} while (--j);
} while(--i);
}
void main()
{
clock_init();
while(1)
{
uart_init_18m_9600();
DetectItrim(" 18MHz 9600\r\n");
uart_init_22m1184_9600();
DetectItrim(" 22.1184MHz 9600\r\n");
uart_init_24m_9600();
DetectItrim(" 24MHz 9600\r\n");
uart_init_28m_9600();
DetectItrim(" 28MHz 9600\r\n");
uart_init_32m_9600();
DetectItrim(" 32MHz 9600\r\n");
uart_init_33m1776_9600();
DetectItrim(" 33.1776MHz 9600\r\n");
}
}
我手里的这块STC8A8K64S4实测结果是这样的
IRTRIM LIRTRIM FOSC BAUD
0E 02 18MHz
53 03 22.1184MHz
74 03 24MHz
F3 02 32MHz
FC 01 33.1776MHz