最近搞了搞STM8L系列的板子,感觉有些地方和S系列的不太一样,简单总结了相关外设的配置方法,相关的驱动都是可以运行的,详细内容如下
RCC时钟
概述:
系统时钟有四个时钟源,高速外部,高速内部,低速外部,低速内部,
上电系统默认的时钟源为高速内部时钟,时钟频率为
2M(16M/8).
HSI
:
16M
高速内部
RC
振荡器
HSE :
1-16M
高速外部晶体振荡器
LSE
:
32.768K
低速外部晶体振荡器
LSI
:
38K
低速内部振荡器
STM8L CLOCK寄存器:
时钟分频寄存器
:
CLK_CKDIVR
(
系统时钟的分频系数设置
)
实时时钟寄存器
:
CLK_CRTCR
(RTC
时钟源及分频系数,
RTC
忙状态
)
内部时钟寄存器
:
CLK_ICKCR
(
内部时钟开关及状态
)
二个外设时钟门控寄存器
:
CLK_PCKENR1/CLK_PCKENR2
(
相关外设的时钟开关
)
CCO
寄存器
:
CLK_CCOR
(
时钟输出
)
外部时钟寄存器
:
CLK_ECKCR
(
外部时钟开关及状态
)
系统时钟状态寄存器
:
CLK_SCSR
(
系统时钟源选择
)
系统时钟切换寄存器
:
CLK_SWR
(
系统时钟源切换
)
切换控制寄存器
:
CLK_SWCR
CSS
寄存器
:
CLK_CSSR
(
时钟安全系统
)
蜂鸣器时钟寄存器
:
CLK_CBEEPR (
时钟源选择及切换忙状态
)
HSI
校准寄存器
:
CLK_HSICALR
HSI
时钟校准微调寄存器
:
CLK_HSITRIMR
HSI
解锁寄存器
:
CLK_HSIUNLCKR
主调节器控制状态寄存器
:
CLK_REGCSR
(时钟源的开启状态)
系统时钟分频系数,默认值为
0x03(8
分频
)
,即默认值为
2M(16M/8)
每个外设有相应的时钟开关,与
stm32
一样,在使用相应外设之前要打开时钟,寄存器为
CLK_PCKENR1
和
CLK_PCKENR2
时钟源选择,我们可以使用默认值选择高速内部时钟
void InitSystemClock( void )
{
CLK_CKDIVR = 0x00;//
系统时钟分频
/*
打开全部的外设时钟*/
CLK_PCKENR1 |= 0xff;
CLK_PCKENR2 |= 0xff;
}
STM8L
与
STM8S
的时钟部分没有太大的区别,需要注意的就是
STM8L
的外设时钟是关闭的(为了降低功耗),而
STM8S
的外设时钟是打开的,这个大家看一下相关寄存器的复位值就可以了,因此在使用
stm8l
的时候要记得打开相应的外设时钟
GPIO
IO
的配置比较简单,
STM8L
和
STM8S
没有什么区别,总共也就那么几个寄存器
STM8L GPIO
寄存器
一个数据方向寄存器
:
Px_DDR
一个输出数据寄存器
:
Px_ODR
一个输入寄存器
:
Px_IDR
二个控制寄存器
:
Px_CR1/Px_CR2
可选择的输入模式:浮动输入和带上拉输入
可选择的输出模式:推挽式输出
关于复用功能,
IO口寄存器中没有特定的给出,我们在用到相关复用功能时,在相应外设中进行设置
端口输出寄存器
端口输入寄存器
端口方向寄存器
配置
IO的时候要注意一下下面这两个寄存器,CR1主要选择输入输出的相应模式,CR2配置输出速度,
要注意一下在适应外部中断的时候,相应的
IO口的CR2要开启外部中断
void GPIOConfigure(void)
{
/*LED
灯
(
用作
pwm
呼吸灯配置输入模式
)*/
PE_DDR &= ~0X80;
PE_CR1 |= 0X80;
PE_CR2 |= 0X00;
#if 0
/*UART
硬件串口可以不用配置
*/
PC_DDR |= 0X08; //
输出
TX---PC3
PC_ODR |= 0X08; //
拉高置
1
PC_CR1 |= 0X08; //
推挽输出
PC_CR2 |= 0X00; //10M
PC_DDR &= ~0X04; //
输入
RX---PC2
PC_CR1 |= 0X04; //
上拉输入
#endif
/*EXIT---PB4---PB5*/
PB_DDR &= ~0X10; //
输入
PB_CR1 |= 0X10; //
上拉输入
PB_CR2 |= 0X10; //
中断使能
PB_DDR &= ~0X20; //
输入
PB_CR1 |= 0X20; //
上拉输入
PB_CR2 |= 0X20; //
中断使能
/*PWM---TIM2_CH2*/
PB_DDR |= 0X04;
PB_CR1 |= 0X04;
PB_CR2 |= 0X04;
PB_ODR |= 0X04;
/*led
灯
*/
PC_DDR |= 0X80; //
输出
PC_ODR |= 0X80; //
拉高置
1
PC_CR1 |= 0X80; //
推挽输出
PC_CR2 |= 0X80; //10M
}
GPIO的复用功能
STM8L内部集成了控制端口复用的寄存器,不想STM8S那样需要在stvp工具中设置,这一点是很方便的,配置起来也很简单,只需要简单的几个寄存器就可以了。
以串口为例,串口默认的接口为PC3和PC2,我们可以执行SYSCFG_RMPCR1 |= 0X20;就可以用PC5和PC6来进行串口通讯。
void GPIO_AF(void)
{
SYSCFG_RMPCR1 |= 0X20;
}
定时器
概述:
STM8L15X
芯片有三种不同类型的定时器
:
高级控制型:Timer1
;通用型
:Timer2/3/5
;基础型
:Timer4
不同的定时器功能有所差异,无非也就是输入捕获,输出比较,更新溢出,PWM
等,配置定时器必须要做的就是
(时钟分频
---
重装载
---
计数方式
---
中断使能
---
开启计数)
有其他功能就再配置其他的寄存器,
PWM
要配置起模式,通道,有效电平以及占空比
相关的寄存器默认值就行很方便
bit7:
预装载使能
bit6:
对齐方式
bit4:
计数方向
bit0:
计数使能
时钟源分频系数,
3
个
bit
,分频值为(
1--128
中
2
的整数次幂)
重装载寄存器,发生溢出事件后自动重新装载
bit7:
BREAK
中断
bit6:
触发中断
bit2:
捕获比较
2
bit1:
捕获比较
2
bit0:
更新中断
定时器
2有两路PWM(捕获比较通道)
寄存器有TIMx_CCMR1和TIMx_CCMR2在输入和输出模式下配置功能也不一样,以下介绍输出模式下的配置
bit4--bit6
PWM
模式
bit3:
预装载
bit0--bit1:
通道输入输出
bit4--bit5:
配置通道
2
bit1:
有效电平
bit0:
输出使能
该寄存器集成了通道
1
和通道
2
的配置
void InitTimer2( void )
{
TIM2_CR1 |= 0x10; //
向下计数
TIM2_PSCR = 0X07; //128
分频
TIM2_ARRH = 0X07; //
重装载值
TIM2_ARRL = 0XD0;
TIM2_CCMR2 |= 0X70; //PWM2----
使能预装载
----
通道
1
输出
TIM2_CCER1 |= 0X10; //
高电平有效
-----
通道输出使能
TIM2_CCR2H = 0X03;
TIM2_CCR2L = 0XE8;
TIM2_BKR = 0x80; //
必须加,使能通道输出
TIM2_CCR = (TIM2_ARRH << 8 | TIM2_ARRL);
TIM2_IER = 0x01; /*
允许更新中断
*/
TIM2_CR1 |= 0x01; /*
计数器使能,开始计数
*/
}
需要注意的是在配置定时器2
的
PWM
功能时,
STM8L
比
STM8S
要多配置一个寄存器
TIM2_BKR = 0x80; //
必须加,使能通道输出
配置时也要讲相应的通道IO
进行配置,相关的时钟开关也要打开
串口
串口配置有以下几处:
数据字长,奇偶检验,停止位个数,波特率配置
使用过程中要注意相应的状态为(发送完成,接收完成等)
状态寄存器USART_SR
;
数据寄存器USART_DR
;
波特率寄存器1 USART_BRR1
;
波特率寄存器2 USART_BRR2
;
控制寄存器1 USART_CR1;
控制寄存器2 USART_CR2;
控制寄存器3 USART_CR3;
控制寄存器4 USART_CR4;
控制寄存器5 USART_CR5;
保护时间寄存器 USART_GTR
;
分频寄存器 USART_PSCR
;
bit4:
字长
bit2:
校验使能
bit1:
奇偶校验
bit0:
校验中断
bit7:
发送空中断
bit6:
发送完成中断
bit5:
接收中断
bit3:
发送使能
bit2:
接收使能
bit4--bit5:
停止位
需要注意的是波特率寄存器对接收和发送是一样的,通过对
BRR1
和
BRR2
的编程实现。写
BRR2
应当先于写
BRR1
,因为写
BRR1
会更新波特率计数器。
void UART_Init(void)
{
USART1_CR1=0x00; //
设置
M
字长,
8
位数据位
USART1_CR2=0x2c; //
使能发送、接收;
USART1_CR3=0x00; //1
位停止位
USART1_BRR2=0x03; //
设置波特率为
9600
USART1_BRR1=0x68;
}
外部中断
关于外部中断,STM8L
和
STM8S
的区别还是比较大的,
STM8L
可以对具体的某一位开启外部中断,也可以对一组
IO
开启中断,而
STM8S
只能对一组
IO
开启中断,这也是
STM8L
低功耗的表现,相应的
STM8L
也多了几个外部中断向量,这个可以在头文件中看到。
在配置外部中断时我们首先要
CPU_CCR = 0X28;I1 I0
全部写
1
时才可以配置其他的寄存器
下面的程序是配置
PB4的外部中断
1
:
CPU_CCR
(
I1 I0
全部写
1
)
2
:
EXTI_CR2
(配置
PB4
)
STM8L
提供了
EXTI_CR1
和
EXTI_CR2
两个寄存器来配置每组
IO
的某个位的中断边沿
EXTI_CR1
:配置每组
IO
的
bit0---bit3
EXTI_CR2
:配置每组
IO
的
bit4---bit6
void InitEXTI( void )
{
CPU_CCR = 0X28;
EXTI_CR2 |= 0x02;//
下降沿触发
}
#pragma vector = EXTI4_vector
__interrupt void EXTI1_ISR( void )
{
if(EXTI_SR1 == 0X10)//
外部中断
4
产生
{
EXTI_SR1 = 0x10;
GPIOC_BIT7_FLASH;
EXTI_FLASH = 1;
}
}
如果要配置
PB口的外部中断我们需要执行
1
:
EXTI_CR3 |= 0x02;//
下降沿触发
2
:
EXTI_CONF |= 0X02;
STM8L
提供了两个寄存器来配置每组
IO
的中断
EXTI_CR3
和
EXTI_CR4
EXTI_CR3
:配置端口
BDEF
EXTI_CR4
:配置端口
GH
另外要配置整组的中断,我们还需要进行中断向量的配置
EXTI_CONF
此寄存器的复位值为0x00
,默认我们的
IO
口的中断向量为
EXTI0----EXTI7(
所以上面程序中我们并没有对这个寄存器操作,因为默认就是
EXTI4)
,如果要使用
EXTIA----EXTIF(
某些
IO
没有
)
我们还需要配置此寄存器
相应的产生中断标志位的寄存器也是不一样,上面的是EXTI_SR1
,这里是
EXTI_SR2
EXTI_SR1
EXTI_SR2
void InitEXTI( void )
{
CPU_CCR |= 0X28;
EXTI_CR3 |= 0x02;//
下降沿触发
EXTI_CONF |= 0X02;
}
#pragma vector = EXTIB_vector
__interrupt void EXTI1_ISR( void )
{
if(EXTI_SR2 == 0X01)//
外部中断
4
产生
{
EXTI_SR2 = 0x01;
GPIOC_BIT7_FLASH;
EXTI_FLASH = 1;
}
}
另外还要注意一点,在配置中断前关全局中断,配完相应的外设中断后,再开全局中断,这是一个标准操作,如果在外部中断配置前开全局中断,会导致
CPU_CCR
的值无法写入
配置的外部中断的IO
也要配置为输入模式,并打开
IO
口寄存器的外部中断,另外在产生中断后在中断处理函数中
EXTI_SR1 = 0x10;
要对中断标志位写
1
来清除中断标志
下面的程序配置了
PB4
和
PB5
,相对应的中断向量也不同
EXTI4_vector
和
EXTI5_vector
void InitEXTI( void )
{
CPU_CCR |= 0X28;
EXTI_CR2 |= 0x0a;//
下降沿触发
bit4--bit5
//EXTI_CR3 |= 0x02;//
下降沿触发
//EXTI_CONF |= 0X02;
}
#pragma vector = EXTI4_vector
__interrupt void EXTI1_ISR( void )
{
if(EXTI_SR1 == 0X10)//
外部中断
4
产生
{
EXTI_SR1 = 0x10;
GPIOC_BIT7_FLASH;
EXTI_FLASH = 1;
}
}
#pragma vector = EXTI5_vector
__interrupt void EXTI1_5ISR( void )
{
if(EXTI_SR1 == 0X20)//
外部中断
4
产生
{
EXTI_SR1 = 0x20;
GPIOC_BIT7_FLASH;
//EXTI_FLASH = 1;
}
}
AD转换
ADC
配置寄存器
1 ADC_CR1
ADC
配置寄存器
2 ADC_CR2
ADC
配置寄存器
3 ADC_CR3
ADC
状态寄存器
ADC_SR
ADC
数据高位寄存器
ADC_DRH
ADC
数据低位寄存器
ADC_DRL
ADC
高阈值高位寄存器
ADC_HTRH
ADC
高阈值低位寄存器
ADC_HTRL
ADC
低阈值高位寄存器
ADC_LTRH
ADC
低阈值低位寄存器
ADC_LTRL
ADC
通道序列寄存器
1 ADC_SQR1
ADC
通道选择扫描寄存器
2 ADC_SQR2
ADC
通道选择扫描寄存器
3 ADC_SQR3
ADC
通道选择扫描寄存器
4 ADC_SQR4
ADC
触发禁能
1-4 ADC_TRIGR1/2/3/4
具体的转换通道要看手册,开始转换后要等待几,
转换完成后,读取数据会清除相应的标志位,当不选择触发输入时,要
ADC1_SQR3 |= 0X40; //
选择扫描通道,不然无法转换
bit5--bit6:
精度配置
bit4:
模拟看门狗
bit2:
连续
/
单次
bit1:
开始
bit0:
开关
bit5--bit7:
采样时间
bit0--bit4:
转换通道
选择要扫描的通道
unsigned intReadSTM8AD(char idx )
{
ADC1_CR1 |= 0X01; //
使能
ADC
ADC1_CR3 |= idx; //
选择转换通道
ADC1_SQR3 |= 0X40; //
选择扫描通道
ADC1_CR1 |= 0x02; //
开始转换;
asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
while(!(ADC1_SR & 0X01));
return ADC1_DRH;
}
EEPROM
eeprom
来说,
stm8L
和
stm8S
的寄存器配置都是一样的,只是
eeprom
的起始地址不一样,起始地址为
0x1000
顺序写入FLASH_DUKR=0xAE;FLASH_DUKR=0x56;
来进行解锁,
void InitEEPROM(void)
{
/*
解锁
*/
do
{
FLASH_DUKR=0xAE;
FLASH_DUKR=0x56;
}while((FLASH_IAPSR & 0x08)== 0x00);
}
unsigned char Stm8lEepromRead( unsigned char ucAddress )
{
unsigned char ucReturn;
unsigned char *p;
p = (unsigned char *)(0x1000 + ucAddress * 0x04);//
读数据
ucReturn = *p;
return(ucReturn);
}
void Stm8lEepromWrite( unsigned char ucAddress, unsigned char ucData )
{
unsigned char *p;
p = (unsigned char *)(0x1000 + ucAddress * 0x04);//
转换地址
*p = ucData; //
写数据
while((FLASH_IAPSR & 0x04) == 0x00); //
等待写操作完成
}
独立看门狗
IWDG_KR
用来配置开启看门狗,重装载,解锁控制命令
时钟源是低速内部时钟,在此寄存器中分频
重装载数值寄存器,在往
IWDG_KR
中写
0xAA
时重新装载喂狗
void IWDG_SetPrescaler(void)
{
IWDG_PR = 0x06;//
时钟源分频
}
void IWDG_SetReload(void)
{
IWDG_RLR = 0xFF;//
看门狗重装载值
}
void IWDG_Refresh(void)
{
IWDG_KR = (unsigned int)0xAA;//
重装载看门狗
}
void IWDG_Enable(void)
{
IWDG_KR = (unsigned int)0XCC;//
开启看门狗
}
void IWDG_Access(void)
{
IWDG_KR = (unsigned int)0X55;//
允许访问看门口狗寄存器
}
void IWDG_Configuration(void)
{
IWDG_Enable();
IWDG_Access();
IWDG_SetPrescaler();
IWDG_SetReload();
IWDG_Refresh();
}