本帖最后由 芯圣电子官方QQ 于 2023-7-24 14:43 编辑
(一)系统初始化
系统时钟采用内部高频晶振32M,经过2分频产生OSC时钟源:
CLKSWR = 0x51; //选择内部高频时钟为主时钟,且内部高频RC2分频
CPU时钟是OSC时钟经过4分频,4M主频:
系统配置:
void System_init(void)
{
WDTCCR = 0x00; //关闭看门狗
while((CLKCON&0x20)!=0x20); //等待内部高频晶振起振
CLKDIV = 0x02; //CPU时钟2分频,确保在进行RC32分频时CPU时钟小于20M
CLKSWR = 0x51; //选择内部高频时钟为主时钟,且内部高频RC2分频
while((CLKSWR&0xC0)!=0x40); //等待内部高频切换完成
CLKDIV = 0x04; //CPU时钟4分频
}
(二)IO口配置
IO配置不兼容传统51,比如配置P00为推挽输出:
电平设置兼容传统51:
#define P0(i,n) (P0 = (n<<i)|(P0&0xFD))
#define PSET(p,i,n) (p = (n<<i)|(p&0xFD))
(三)UART配置
芯圣单片机的外设是类似NXP新款的那种设计哲学,可以随意指定输出引脚,不用限定IO口,布线灵活,这种设计哲学就比ST的高明一些。
UART外设理论上可以采用下载口P21和P03,但实践证明在S003P的目标板上,这种配置行不通,一旦串口与串口软件连接,单片机立即停止运行。
因此我采用P01、P02口。
void UartInit(void)
{
// P2M0 = P2M0&0x0F|0x80; //P21设置为推挽输出
// P0M1 = P0M1&0x0F|0x20; //P03设置为上拉输入
P0M0 = P2M0&0x0F|0x80; //P01设置为推挽输出
P0M1 = P0M1&0xF0|0x02; //P02设置为上拉输入
TXD_MAP = 0x01; //TXD映射P21
RXD_MAP = 0x02; //RXD映射P03
T4CON = 0x06; //T4工作模式:UART1波特率发生器
TH4 = 0xFF;
//TL4 = 0x98; //波特率9600
TL4 = 0xef; //波特率57600
SCON2 = 0x02; //8位UART,波特率可变
SCON = 0x10; //允许串行接收
IE |= 0X10; //使能串口中断
EA = 1; //使能总中断
}
关于波特率推算,可以参考数据手册96页。
中断服务函数,事先定义结构体:
struct _UartRecStruct{
unsigned char flag; //UART(完成)判断标志位
unsigned char counter; //UART(接收个数)计数使用
unsigned char Uartbuf[30];//用于存放接收收据
};
void UART1_Rpt(void) interrupt UART1_VECTOR
{
if(SCON & 0x01) //判断接收中断标志位
{
if(Uart1Rec.flag == 0)
{
Uart1Rec.Uartbuf[Uart1Rec.counter] = SBUF;//转存8位串口接收数据
if(Uart1Rec.counter>=50 ||
Uart1Rec.Uartbuf[Uart1Rec.counter]=='\n')
{
SCON &=~ 0x10; //失能UART1接收
Uart1Rec.flag = 1;
}
else
Uart1Rec.counter++;
}
SCON &=~ 0x01; //清除接收中断标志位
}
}
发送函数:
void SendChar(char *Uartbuf, unsigned int num)
{
unsigned int send_i=0;
IE &=~ 0x10; //失能UART1中断
for(send_i= 0;send_i<=num;send_i++)
{
SBUF = Uartbuf[send_i];//发送8位串口数据
while(!(SCON & 0x02));
SCON &=~ 0x02; //清除发送中断标志位
}
IE |= 0x10; //UART1中断使能
}
输出重定向:
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
SendChar(f, (uint8_t)ch);
return ch;
}
主循环内操作读取到的信息,重新打印到上位机:
if(Uart1Rec.flag)
{
SendChar(Uart1Rec.Uartbuf, Uart1Rec.counter);
Uart1Rec.flag = 0;
Uart1Rec.counter = 0;
SCON |= 0x10; //UART1接收使能
}
(四)ADC配置
adc首先要配置引脚模拟输入:
P0M0 = P0M0&0xF0|0x03; //P00模拟输入
三个控制寄存器:
//1(使能adc)0(启动转换)0(中断标志位)0(保留位)
//0(不输出参考电压)0(选内部参考电压)10(内部3v)
ADCC0 = 0x82;
Delay_ms(1);
//00(禁止内部通道接入)00(保留位)0000(AN0通道)
ADCC1 = 0;
//01(转换12位,右对齐)001(2/1M的配置)011(32/2=16八分频=2M)
ADCC2 = 0x4D;
读取adc:
int ADCRead(void)
{
int num=0,i=0;
char send_c[20]={0};
Delay_ms(1);
//清除标志位
ADCC0 &= 0xDF;
//置位启动转换
ADCC1 |= 0x40;
sprintf(send_c, "adc begin\r\n");
SendChar(send_c, strlen(send_c));
while((ADCC0&0x20)!=0x20){
// i++;
// if(i<=20000)
// return -1;
}
sprintf(send_c, "adc finish\r\n");
SendChar(send_c, strlen(send_c));
ADCC0 &=~ 0x20;
//读取数据
num = ADCRH;
num = num<<8;
num |= ADCRL;
return num;
}
(五)整体设置
读取ADC值,上传:
send_d = ADCRead();
sprintf(send_c, "adc:%d\r\n", send_d);
SendChar(send_c, strlen(send_c));
超过阈值,点亮警示灯:
P0M2 = 0x80|(P0M0&0xF0);
if(send_d >= 0x8ff)
PSET(P0,4,1);
else
PSET(P0,4,0);
|