PIC16F系列单片机接口程序设计经典案例

本文最后更新于 2024年1月27日 下午

PIC16F系列单片机接口程序设计经典案例

针对Brunel University:2021 EE2623 Computer Architecture and Interfacing 的期末复习笔记
Lecturer: Dr. Itagaki Takebumi(板垣 剛文)/Dr.Hongying Meng(孟鸿鹰)
程序仅包含主函数部分。

输入接口

简单开关

1
2
3
4
5
6
7
8
9
10
11
while(1)
{
if((porta&1)==0)
{
delay(5); //避免switch bounce
if((porta&1)==0)
{
... // 触发开关后的操作
}
}
}

键盘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void main()
{
adcon1 = 0x06; //初始化
trisa = 11110000b; //设置porta一半输入一半输出
porta = 0xff; //porta=1111 1111
while(1)
{
clear_bit(porta,0); //porta=1111 1110 切换到第一行扫描
if((porta&0x10)==0) // porta=1110 1110
{
value = '1';
}
if((porta&0x20)==0) // 键盘:为0按下,为1弹起
{
value = '2';
}
if((porta&0x40)==0)
{
value = '3';
}
set_bit(porta,0);
clear_bit(porta,1); //porta=1111 1101 切换到第二行扫描
if((porta&0x10)==0) //porta=1110 1101
{
value = '4';
}
if((porta&0x20)==0)
{
value = '5';
}
if((porta&0x40)==0)
{
value = '6';
}
...
}
}

显示接口

LED

闪烁LED

1
2
3
4
5
6
7
8
9
10
void main()
{
trisb = 0; //设置portb为输出
while(1)
{
portb = 0xff; // LED 灯: 为0熄灭,为1亮起
delay(10);
portb = 0;
}
}

流水灯

1
2
3
4
5
6
7
8
9
void main()
{
trisb = 0; //设置portb为输出
while(1)
{
portb = portb << 1; // 左移一位
delay(10);
}
}

八位数码管

一位数字显示

1
2
3
4
5
6
7
char seg8(char value)
{
if(value == 0) return(0xc0); //八位数码管的比特位控制见讲义中图片
if(value == 1) return(0xf9); // 八位数码管:为0亮,为1不亮
...
if(value > 9) return(0xff);
}

四位数显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void display(int value)
{
portb = seg8(value / 1000); // 得到千位数字
set_bit(porta,3); // porta负责控制哪一个八位数码管亮起(1为亮)
delay(4);
value = value % 1000; // 求余数
clear_bit(porta,3);
portb = seg8(value / 100);
set_bit(porta,2);
delay(4);
value = value % 100;
clear_bit(porta,1);
portb = seg8(value / 10);
set_bit(porta,1);
delay(4);
value = value % 100;
clear_bit(porta,2);
portb = seg(value % 10);
set_bit(porta,0);
delay(4);
clear_bit(porta,0);
}

LCD

初始化

1
2
3
4
5
6
7
8
9
10
11
void lcd_init()
{
// 本质上来说是向portb重复发送一些东西,0x23和0x03可以自由设定。
delay(10);
portb = 0x23;
portb = 0x03;
delay(10);
portb = 0x23;
portb = 0x03;

}

发送指令

1
2
3
4
5
6
7
void lcd_cmd(char cmd)
{
portb = 0x20+((cmd>>4)&0x0f); //开启(0x20)+指令头四位
portb = (cmd>>4)&0x0f; //移动指令头四位到后四位,并清除原来头四位
portb = 0x20+(cmd&0x0f); //开启(0x20)+指令后四位
portb = cmd & 0x0f; //清除指令头四位
}

发送单个字符

1
2
3
4
5
6
7
void lcd_char(char c)
{
portb = 0x30 + ((c>>4)&0x0f); //开启(0x30)+指令头四位
portb = 0x10 + ((c>>4)&0x0f); //移动指令头四位到后四位,并清除原来头四位
portb = 0x30 + (c&0x0f); //开启(0x30)+指令后四位
portb = 0x10 + (c&0x0f); //清除指令头四位
}

显示字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void display_message(const char* message)
{
int i = 0;
while(message[i] != 0)
{
lcd_char(message[i]);
i++;
}
}
void main()
{
adcon1 = 0x06; //设置adcon1接收来自portA的所有pins的数字信号
trisa = 0xf8; //设置portA的PA0,PA1,PA2为输出
trisb = 0x00; //设置PortB为输出
lcd_cmd(1); //清除显示
lcd_init(); //初始化LCD
lcd_cmd(0x38); //设置两行,8比特,5x7点阵
lcd_cmd(0x0c); //设置打开显示,不显示光标
lcd_cmd(0x06); //设置光标右移
lcd_cmd(1); //清除显示
while(1)
{
lcd_cmd(0x80); // 第一行第一个光标位置的地址:0x00(地址)+0x80(指令)
display_massage("Hello");
lcd_cmd(0xc0); // 第二行第一个光标位置的地址:0x40+0x80
display_message("world");
}
}

LCD在显示数字时,一定要将数字转换为ASCII码,具体操作为:display_char('0'+number);

数模转换器 ADC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void convert(char channel)
{
adcon0 = 11000001b + (channel<<3); //设置channel
set_bit(adcon0, GODONE); // 清除GODONE,开始转换
while((adcon0 & 00000100b) !=0); //等待到转换结束
return((adresh<<8)+adresl); //返回得到的10比特值
}
void main()
{
int value;
adcon1 = 0x80; //设置8个通道都是模拟信号
adcon0 = 11000001b; //设置转换速度
while(1)
{
value = covnert(4);
}
}

计时器

延迟函数(通用)

1
2
3
4
5
6
7
8
void delay(int j)
{
int i;
for(; j!=0; j--)
{
for(i=8333; i!=0; i--); //执行一次循环需要12us 8333*12≈0.1s
}
}

利用溢出设置延迟

1
2
3
4
5
6
7
8
9
10
11
12
void delay(int x)
{
tmr0 = 0; //重置timer0
clear_bit(intcon,TOIF); //重置溢出flag
while(x!=0)
{
while((intcon & 00000100b) == 0); //等到溢出
clear_bit(intcon, T0IF); //重置溢出flag
x--;
}

}

如果Fosc/4=0.2s, Pre-scalar=1:32,x=61时,能造成 0.2x32x256usx61=0.0999424s的延迟

中断程序

中断程序的时间控制

1
2
3
4
5
6
void interrupt()
{
clear_bit(intcon,T0IF); //重置溢出flag
tmr0 = 100; //给timer0设置初始值,此时一个中断运行的时间是256-100=156us
··· // 中断内的其他操作
}

PWM 控制马达

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
TMR0 = 246; // 中断发生的时间为256-246=10us
int cycle; // 一个周期的时间,时间设定在中断程序中设置
int pwm = 400; // 一个周期内马达开启的时间,此处时间为400 x 10us=4ms
void interrupt()
{
cycle--;
if(cycle == 0)
{
cycle = 1000; // 此处一个周期的时间为1000 x 10us=10ms
}
if(pwm == 0) // 如果pwm没有设置
{
pulse = 0;
clear_bit(portb,7); // 关闭转子,转子不会工作
}
else
{
pulse = pwm; // 转子开启的时间
set_bit(portb,7); // 开启转子
}
else
{
if(pulse != 0)
{
pulse--; // pulse 自减
}
if(pulse == 0)
{
clear_bit(portb,7); //进入转子的关闭期
}
}
}

PIC16F系列单片机接口程序设计经典案例
https://l61012345.top/2021/06/10/学习笔记/计算机结构与接口/3. 经典PIC16F单片机程序设计案例/
作者
Oreki Kigiha
发布于
2021年6月10日
更新于
2024年1月27日
许可协议