一,Seg
底层:
高电平灭,共阳极
‘,’的ASCII码是44
不消隐的话:
显示完3,想显示5:显示5时有视觉残留
消隐:
显示完3,想显示5:流程:先显示3,然后显示空白,最后显示5
#include "seg.h"
// 0-9 灭
// A
code unsigned char seg_dula[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92,0x82, 0xf8, 0x80, 0x90, 0xff,
0x88};
void Seg_Disp(unsigned char wela, unsigned char dula, unsigned char point) {
// 消隐
P0 = 0xff;
P2 = P2 & 0x1f | 0xe0;
P2 &= 0x1f;
// 位选
P0 = 0x01 << wela;
P2 = P2 & 0x1f | 0xc0;
P2 &= 0x1f;
// 段选
P0 = seg_dula[dula];
if (point) P0 &= 0x7f;
P2 = P2 & 0x1f | 0xe0;
P2 &= 0x1f;
}
void Timer1_Isr(void) interrupt 3:
// 数码管显示处理
if (Seg_Buf[Seg_Pos] > 20)
Seg_Disp(Seg_Pos, Seg_Buf[Seg_Pos] - ',', 1); // 带小数点
else
Seg_Disp(Seg_Pos, Seg_Buf[Seg_Pos], 0); // 无小数点
void Seg_Proc():
Seg_Buf[0] = 9 + ',';
二,c语言里的结构体,c++里的类
三,本质上调度器还是从上到下运行,得把数码管放最后面
保证显示的数据,是获取之后的数据
不然:
数据还没更新,或数据更新了,但没丢到数码管里,
数码管显示的数据(可能是上次的数据)和真实的数据不一样
四,led可以同时点亮(不要1ms点亮一个)
好处:led的pwm,亮度等级
底层:
输入:ucLed[]={1,0,1,0,1,0,1,0};
Temp_1=0101 0101
idata unsigned char temp_1 = 0x00;
idata unsigned char temp_old_1 = 0xff;
void Led_Disp(unsigned char *ucLed)
{
temp_1=0x00;
temp_1 = (ucLed[0] << 0) | (ucLed[1] << 1) | (ucLed[2] << 2) | (ucLed[3] << 3) |
(ucLed[4] << 4) | (ucLed[5] << 5) | (ucLed[6] << 6) | (ucLed[7] << 7);
if (temp_1 != temp_old_1)
{
P0 = ~temp_1;
P2 = (P2 & 0x1f) | 0x80;
P2 &= 0x1f;
temp_old_1 = temp_1;
}
}
void Led_Off()
{
P0 = 0xff;
P2 = (P2 & 0x1f) | 0x80;
P2 &= 0x1f;
temp_old_1=0x00;
}
void Timer1_Isr(void) interrupt 3:
idata unsigned char pwm_period=10; //pwm调光周期,单位为毫秒
idata unsigned char pwm_compare=5; //pwm调光占空比,0-10可调,初始为5
if(++pwm_period==10)pwm_period=0;//pwm计数自增
if(pwm_period>=pwm_compare)//pwm计数比较
{
Led_Disp(ucLed); // LED显示更新
}
else//熄灭led
{
Led_Off();
}
void Led_Proc():
void Led_Proc()
{
// Led处理逻辑可以在这里添加
//如题目没有要求pwm调光时,可将Led_Disp函数放到此处刷新led以减少定时器1中断的执行时间
//Led_Disp(ucLed); // LED显示更新
}
五,中断一般都是运行判断,也可以运行处理函数,最好别。
中断时间过长影响代码
六,filtering硬件的问题,软件滤波来弥补(防止测到的数据突变)
冒泡排序:
通过多次遍历,未排序部分的最大(或最小)元素会逐渐“浮”到列表的顶端,就像气泡一样
内循环一次,把最大的数 排到 最高位
外循环十次,十个数小到大(这里:sorted_data[j] > sorted_data[j + 1],交换位置) 依次排
sorted_data[10]={低, , , , , , , , ,高};
中值滤波函数:
代码有问题:刚进一个数据,滤波后,中间值不是就0了吗
群友改进:(不一定对)
数据个数>=3,用中值滤波,
数据个数<3,用第一个数据替代
#include <Filtering.h>
#include <string.h>
#define N 10 // 滤波窗口大小
pdata int data_array[N]={0}; // 存储窗口内的数据
pdata int index = 0; // 当前数据的索引
// 中值滤波器函数
// 参数:new_data - 新的输入数据
// 返回值:滤波后的数据
int Median_Filter(int new_data)
{
idata int sorted_data[N];
idata int i, j, temp;
// 复制数据到临时数组
for (i = 0; i < N; i++) {
sorted_data[i] = data_array[i];
}
// 冒泡排序
for (i = 0; i < N - 1; i++)
{
for (j = 0; j < N - i - 1; j++)
{
if (sorted_data[j] > sorted_data[j + 1])
{
// 交换元素位置
temp = sorted_data[j];
sorted_data[j] = sorted_data[j + 1];
sorted_data[j + 1] = temp;
}
}
}
data_array[index] = new_data; // 存储新数据
index = (index + 1) % N; // 更新索引
return sorted_data[N / 2]; // 返回中值
}
滑动平均滤波函数:
计算信号在一定窗口内的平均值来替代当前采样值,从而抑制高频噪声。
这里返回平均值有问题:应该这样 没十个时,有几个除几,满了后,除十
Index_2初始为0,下面代码不一定对
if(Index_2<N)
return sum/Index_2;
else
return sum/N;
#define N 10 // 滤波窗口大小
pdata int data_array_2[N]={0}; // 存储窗口内的数据
pdata int sum = 0; // 存储窗口内数据的和
pdata int index_2 = 0; // 当前数据的索引
// 滑动平均滤波器函数
// 参数:new_data - 新的输入数据
// 返回值:滤波后的数据
unsigned int Moving_Average_Filter(unsigned int new_data)
{
sum -= data_array_2[index_2]; // 减去窗口中被替换的数据
data_array_2[index_2] = new_data; // 存储新数据
sum += new_data; // 计算新的和
index_2 = (index_2 + 1) % N; // 更新索引
return sum / N; // 返回平均值
}
七,温度,中值滤波(排出去,找一段时间的中值),让温度低图形更加平滑
idata unsigned int temputure_100x; // 温度数据x100
/*温度读取函数*/
void Get_temputure(void)
{
unsigned int temp = rd_temperature() * 100;
temputure_100x= Median_Filter(temp);
}
八,超声波,滑动平均滤波,先排序(一个一个值丢),避免超声波突然变化
超声波数据,一直流进数组(10个值),流进一个到数组,把最旧的数值丢了
数组返回平均值
/*超声波测距读取函数*/
void Get_distance(void)
{
unsigned int temp = Ut_Wave_Data();
distance= Moving_Average_Filter(temp);
}
九,STARTUP.A51是51的启动文件(新建工程时,问要不要添加进去):
负责初始化堆栈、清除 RAM、配置硬件环境,并最终跳转到 main 函数。
通过自定义 STARTUP.A51,可以优化单片机的启动行为,确保程序能够正确运行。
debug时用
十,Beep,Motor,Relay底层
必须要用同一个全局变量:不然Beep,Motor,Relay任意两者同时使用时,会数据覆盖,
导致不能同时工作
idata unsigned char temp_0 = 0x00;
idata unsigned char temp_old_0 = 0xff;
/// @brief 蜂鸣器控制
/// @param enable
void Beep(bit enable)
{
if (enable)
temp_0 |= 0x40;
else
temp_0 &= ~(0x40);
if (temp_0 != temp_old_0)
{
P0 = temp_0;
P2 = P2 & 0x1f | 0xa0;
P2 &= 0x1f;
temp_old_0 = temp_0;
}
}
/// @brief 继电器控制
/// @param enable
void Relay(bit enable)
{
if (enable)
temp_0 |= 0x10;
else
temp_0 &= ~(0x10);
if (temp_0 != temp_old_0)
{
P0 = temp_0;
P2 = P2 & 0x1f | 0xa0;
P2 &= 0x1f;
temp_old_0 = temp_0;
}
}
/// @brief MOTOR控制
/// @param enable
void MOTOR(bit enable)
{
if (enable)
temp_0 |= 0x20;
else
temp_0 &= ~(0x20);
if (temp_0 != temp_old_0)
{
P0 = temp_0;
P2 = P2 & 0x1f | 0xa0;
P2 &= 0x1f;
temp_old_0 = temp_0;
}
}
舵机Motor(向外输出高低电平)考点:PWM波
SIG_OUT(P34连接)是测ne555
十一,Key按键8,9同时按下
按列扫描:刚好按键8,按键9在同一列
P44 = 1;P42 = 0;P35 = 1;//P34 = 1;用到ne555,注释掉P34
if(P33 == 0&&P32 == 0) temp = 89;
但按键在调度器10ms扫描一次,10ms内同时按下?
按键扫描时间不能给太长:
按键时间越长,消抖效果越好,但按键生效越晚(按键的反应精度不够),界面切换时间不达标
但10ms按不了双按键,那就改成20ms
万一双按键,既不同行,又不同列
行列扫描:
uint16_t是16位,按键是16个:1位代表一个按键
哪两个按键同时=1,说明哪两个按键同时按下
十二,Key
#include "key.h"
unsigned char Key_Read() {
unsigned char temp = 0;
// ET1 = 0; 串口发送,用到中断,如果按键进去,可能导致中断崩一下,会频繁的开关中断
P44 = 0;
P42 = 1;
P35 = 1;
//P34 = 1;
if (P33 == 0) temp = 4;
if (P32 == 0) temp = 5;
if (P31 == 0) temp = 6;
if (P30 == 0) temp = 7;
P44 = 1;
P42 = 0;
P35 = 1;
//P34 = 1;
if (P33 == 0) temp = 8;
if (P32 == 0) temp = 9;
if (P31 == 0) temp = 10;
if (P30 == 0) temp = 11;
P44 = 1;
P42 = 1;
P35 = 0;
//P34 = 1;
if (P33 == 0) temp = 12;
if (P32 == 0) temp = 13;
if (P31 == 0) temp = 14;
if (P30 == 0) temp = 15;
/*
P44 = 1;
P42 = 1;
P35 = 1;
P34 = 0;
if (P33 == 0) temp = 16;
if (P32 == 0) temp = 17;
if (P31 == 0) temp = 18;
if (P30 == 0) temp = 19;
*/
// P3 = 0xff;//防止串口出问题
// ET1 = 1;
return temp;
}
- 烧录时,蜂鸣器叫:正常现象
十四,ds1302
void Set_Rtc(unsigned char *ucRtc)//十进制存放的
{
unsigned char i;
Write_Ds1302_Byte(0x8e,0x00);
for(i=0;i<3;i++)
{
Write_Ds1302_Byte(0x84-2*i,ucRtc[i]/10%10<<4|ucRtc[i]%10);
或 Write_Ds1302_Byte(0x84-2*i,ucRtc[i]/10%10*16+ucRtc[i]%10);
}
Write_Ds1302_Byte(0x8e,0x80);//写保护是不让写
}
void Read_Rtc(unsigned char* ucRtc)十进制读出
{
unsigned char i,temp;
EA=0;读时间需要关中断(有时候读时间会,时间会跳一个0出来:读时会延时,导致中断乱掉一下)
for(i=0;i<3;i++)
{
temp = Read_Ds1302_Byte(0x85-2*i);//读位置跟写位置 不一样
ucRtc[i] = temp/16%16*10+temp%16;
}
EA=0;
}
- ds18b20
仿真要注意,不要乘12
ds18b20手册第十页:
如果有多个ROM,就会检查,但板子上只有一个ROM,检查需要花时间,所以跳过
float rd_temperature()
{
unsigned char low,high;
init_ds18b20();
Write_DS18B20(0xcc);//跳过ROM
Write_DS18B20(0x44);//启动温度转换
Delay_OneWire(200);//等待转换完成
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);//读取温度值
low = Read_DS18B20();
high = Read_DS18B20();
return (float)((high << 8) | low) / 16.0;//或*0.0625精度值
}
手册第三页:
12位精度 对应 0.0625刻度
(温度传感器用户可配置为9,10,11或12位,对应于0.5°C的增量;分别为0.25℃、0.125℃和0.0625℃。开机时的默认分辨率是12位。)
10位/11位,读取出来后,分辨率降低,但最终还是乘0.0625
(值的精度会变,整体算法不变)
手册第八页:
十六,上位机 发信息 给单片机,用sscanf(按照相对格式读取)在串口函数里 进行字符串 比对
sscanf解析里面的数据
strcmp比较
十七,pdata,idata内存区间的划分,逻辑上可以全用pdata或idata,只差了几纳秒
十八,IIC,PCF8591
读底层:
AD:0~5v到0~255:temp是0~255 写地址,读数字量
unsigned char Ad_Read(unsigned char addr) //写PCF8591;写定位;读PCF8591;读一个数字
{
unsigned char temp;
// 选择芯片为PCF
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
temp = I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return temp;
}
写底层:
是0~255 写数字
void Da_Write(unsigned char dat) //写PCF8591;打开Da通道;写一个数字量
{
// 选择芯片为PCF
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x41);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
}
用法:
idata unsigned int AD_in_100x; // AD输入的数据x100
idata unsigned int DA_out_100x = 100; // DA输出的数据x100
void AD_DA(void)
{
AD_in_100x = Ad_Read(0x43) * 100 / 51;
Da_Write(DA_out_100x * 51 / 100);
}
void Seg_Proc()
{
printf("%u,%u,%u,%u\n", temputure_100x / 100, distance, AD_in_100x / 100, Freq / 1000);
}
十九,写ds1302时
AIN0外部输入,AIN1光敏电阻,AIN2差分输入(没考过),AIN3滑动变阻器
I2CSendByte(0x41);
0100 0001:0x41光敏电阻(开启DA)
0100 0011:0x43滑动变阻器(开启DA)
第零位:0
第一位:1是否开启模拟输出(是否开启DA)开启为1
第二三位:00用AIN0
第四位:0
第五位:0自动递增,先给个0号,下一刻读取1号,下一刻读取2号,我们要手动,所以0
第六七位:读取哪个通道
AIN0外部输入,AIN1光敏电阻,AIN2差分输入(没考过),AIN3滑动变阻器
SCH_V31第四页
二十,IIC挂载很多外设:9是1001代表PCF8591
二十一,AT24C02写入四个delay,防止连续写入出问题
二十二,超声波
1/40 000 000=0.000000025s=
超声波需要12.5us,但ISP只能生成12us,自己把33改成38
二十三,超声波读取用PCA定时器
定时器0测ne555,定时器1定时1ms (调度器),定时器2串口
二十四,超声波算0.017时,要把单位化成cm/us
L= V米每秒 * T/2
超声波测距是测量声波发出去和反射回来是时间间隔,用微秒做单位。
空气中的声速是340m/s=3400cm/s=0.034cm/us
时间要÷2
65536μs最大探测距离是65536×0.017=1114厘米=11.14米
二十五,while((Rx==1)&&(TF1==0));//数据读取未结束,没溢出:循环
二十六,uart
底层:
串口12MHZ,串口1,9600,8位数据,定时器2,12T
串口重定向printf
return ch是自带的格式
不用在.h里声明,(可能是因为extern),自动把这个函数编进去
如果自己的代码写了putchar,不会用系统的putchar,而是用自己的
extern char putchar(char ch)
{
SBUF = ch;
while (TI == 0);
TI = 0;
return ch;
}
/* 串口处理函数 */
void Uart_Proc()
{
if (Uart_Rx_Index == 0)
return; // 无数据,直接返回
if (Uart_Recv_Tick >= 10)
{ // 若接收超时,对数据读取,清空缓存区
Uart_Recv_Tick = 0;
if(Uart_Buf[0]==’A’&&Uart_Buf[1]==’B’)
{
//如果接受到AB
}
memset(Uart_Buf, 0, Uart_Rx_Index); // 清空接收数据
Uart_Rx_Index = 0;
}
}
/* 串口1中断服务程序 */
void Uart1_Isr(void) interrupt 4
{
if (RI)
{ // 若接收到数据
Uart_Recv_Tick = 0; // 清零接收时间标志
Uart_Buf[Uart_Rx_Index++] = SBUF; // 将数据保存到缓冲区
RI = 0; // 清除接收中断标志
if (Uart_Rx_Index > 10)
Uart_Rx_Index = 0; // 防止溢出
}
}
二十七,idata访问速度比pdata快一点,可以全用idata(但怕idata爆),xdata比这两个都慢
二十八,key_val和key_old差10ms(为什么)
二十九,keil5,help,uvision help,搜printf,
printf c51library routine,要打印什么数据,有对应的类型
三十,sscanf解析固定格式
发送数据出来,有一个相关的格式,
你是知道我会发这个格式出来的
sscanf读取
sscanf(str,"%d,%d",i,j);
前面的值%d给i,后面的给j
把逗号搞没了
三十一,调度器如果时间撞上了,优先从上往下
led放下面也好一点
每个人板子性能有区别,多少ms调度一次不一定
三十二,初始化顺序有讲究,定时器1要最后初始化,整个系统开始运行
三十三,刚上电延时750ms,让设备跑起来,尤其是ds1302上电读取的时候是85
三十四,
ptr: 指向要填充的内存区域的指针。
value: 要填充的值,以 int 形式传递,但函数会将其转换为 unsigned char。
num: 要填充的字节数。
char str[50];
将 str 数组的所有字节初始化为 'A'
memset(str, 'A', 50);
int arr[10];
将 arr 数组的所有字节初始化为 0
memset(arr, 0, sizeof(arr));