本帖最后由 芯圣电子官方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);
|