|
本帖最后由 w453509596 于 2015-1-9 13:01 编辑 2 n. G' d2 O+ S! C/ Z I 我刚刚使用STM8S几个月,从刚开始到现在会使用,在这过程中也走了很多的弯路,浪费了许多时间。我看到也有一些刚接触STM8的同学和我刚开始一样,对于入门STM8无从下手。我在这里把我入门的过程给分享出来,希望能对想入门STM8S的同学有所帮助。根据我个人的学习经历,感觉初学者最需要的是从一个实际的项目入手,让初学者在不熟悉其全部理论的情况下也可以把东西做出来,然后再回过头来去理解其中的全部原理。我的STM8入门是从一个测线板开始的,我把我的学习过程和其中碰到的问题全部写了来。 1 准备工作: 一块STM8学习板,编程软件,数据手册,参考手册, 一颗坚不可崔的决心。/ f4 w5 z8 ?, ] 想做成一个东西,兴趣、压力、动力,这几点非常重要,我希望朋友们在学习STM8之前能给自己找一个学习的理由,绝对会事办功倍,我当初给自己设了一个理由,我学会了STM8S,就能够涨工资,然后就可以找到个漂亮的女朋友。学习STM8有着他天然的优势,首先这芯片价格便宜(香蕉超市最便宜卖2块2,它最便宜的只要1块5);其次,学习它只需要用到我们最喜爱的语言(chinese Simple);再次它的官方资料非常多,官方库的程序很直观,不像飞思卡尔什么的,各种秀操作。最后还有我们这个强大的社区 (https://www.stmcu.org.cn/)。我当时的资料全部都在这个社区找的。( \2 N# m" t1 ]3 f6 o " a+ I- m! I) d8 V3 I |
STM8自学笔记(推荐STM8很好的入门电子书)
无刷直流电机控制应用+基于STM8S系列单片机---电子书
【精华资料】STM8Sxx固件库FOR IAR
基于STM8的DALI (数字可寻址调光协议)
分享STM8 风驰光盘的资料,是完整的(包括原理图+例程+PDF注释)
《无刷直流电机控制应用 基于STM8S系列单片机》
STM8S库函数中文参考 小软件
【资料分享】STM8L的智能手持血糖监测设备的源码
基于STM8S207工程模板
【培训资料】STM8系列PPT培训资料
微信公众号
手机版
! g5 w" G+ C/ ^0 s
首先编程软件的安装,开发环境的搭建,以及想自己建立工程模板的同学们,请参考附件。我在这里介绍如何驱动我的板子上面的LED。
从原理图上可以看出,我的8颗LED连接PG0~PG7上面,公共端连接在ULN2003A上,由PE0端口驱动。如果我想要第 1 个小灯点亮,只要 PG = 0x01 , PE0 = 1 即可。
如查想做个LED的闪烁效果,大家可以在MAIN文件中加上如下一段简单的程序
void delay(unsigned int t) " v K1 p6 ?# [! p2 u! d
{% `* Z) {/ m* Y7 m6 C' N( O
unsigned char i;1 _# }% l" {# B) [/ B ]8 e
while(t--)- t7 l) K$ i8 f& g
{; c/ p! S' N7 z
for(i=0;i<250;i++);
}
}1 A; N* ]( a# p: p4 L
1 x8 q7 |, Z. X$ I' N+ _. _7 z
void main(void)
{
GPIO_DeInit(GPIOG); //复位G端口寄存器
GPIO_DeInit(GPIOE); //复位E端口寄存器
# d/ L! o: e. w, E* E
GPIO_Init(GPIOG,GPIO_PIN_ALL,GPIO_MODE_OUT_PP_LOW_SLOW); //初始化GPIOG为推挽 低速 输出
//初始电平为低电平
GPIO_Init(GPIOE,GPIO_PIN_0,GPIO_MODE_OUT_PP_LOW_SLOW); //初始化GPIOE0为推挽 低速 输出
//初始电平为低电平
' b! b. \" M' Z; o& d: C" J! c
GPIO_WriteHigh(GPIOE, GPIO_PIN_0); //打开LED的公共端 即 PE0 = 18 G! f$ K+ B$ d6 O/ o
/* Infinite loop */# |9 g! f2 m. l2 n/ T
while (1)+ l( `' ]# C; z6 d. ]
{* X% j7 v W: b' A) l; W6 x; V
GPIO_WriteHigh(GPIOG, GPIO_PIN_0); //点亮第1个LED 即PG0 = 1
delay(1000); //延时一小段时间 B9 D% N5 d& O& L }
GPIO_WriteLow(GPIOG, GPIO_PIN_0); //关闭每1个LED 即PG0 = 0
delay(1000); //延时一小段时间
}* y) j! S- \4 A9 B4 N! k
}; ]# T* f6 _# G8 Y F' s A
& V; \! H" r' v5 \
在这段程序中,单片机使用的是上电复位后默认的初始化(内部16M的参考时钟8分频,即2MHz作为系统主频)。
GPIO_Init(GPIOG,GPIO_PIN_ALL,GPIO_MODE_OUT_PP_LOW_SLOW);函数的原型如下
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_Pin_TypeDef GPIO_Pin, GPIO_Mode_TypeDef GPIO_Mode)# u3 F0 u- @, u
它的功能是初始化一个端口的指定引脚为某种状态) R/ ~2 b- X8 L( n& e5 j7 i
这个函数它有三个参数, : ]) X* c. ~; f; v4 R
第一个 GPIO_TypeDef* GPIOx ( 选择端口)
其中这个 GPIOx 可以替换成 GPIOA 、 GPIOB、 GIPOC、GPIOD、GPIOE、GPIOF
* \) k( _8 a' x- ?! x2 e _
第二个参数是 GPIO_Pin_TypeDef GPIO_Pin (选择引脚) # [$ [5 A# l2 m, M s p- g# B/ y
其中这个 GPIO_Pin 可以替换成下面这些
GPIO_PIN_0 = ((uint8_t)0x01), /*!< Pin 0 selected */
GPIO_PIN_1 = ((uint8_t)0x02), /*!< Pin 1 selected */ g8 j( Y; u, I n' A! v2 |- X
GPIO_PIN_2 = ((uint8_t)0x04), /*!< Pin 2 selected */+ k. t5 ^' [- G R( y* B1 q# z; U
GPIO_PIN_3 = ((uint8_t)0x08), /*!< Pin 3 selected */! }2 U0 g! P7 f. K6 s. h
GPIO_PIN_4 = ((uint8_t)0x10), /*!< Pin 4 selected */4 R4 {0 R- `9 t2 q9 [# [; A. U
GPIO_PIN_5 = ((uint8_t)0x20), /*!< Pin 5 selected */. i! ~. K, U6 b3 o
GPIO_PIN_6 = ((uint8_t)0x40), /*!< Pin 6 selected */
GPIO_PIN_7 = ((uint8_t)0x80), /*!< Pin 7 selected */
GPIO_PIN_LNIB = ((uint8_t)0x0F), /*!< Low nibble pins selected */
GPIO_PIN_HNIB = ((uint8_t)0xF0), /*!< High nibble pins selected */
GPIO_PIN_ALL = ((uint8_t)0xFF) /*!< All pins selected */
第三个参数是 GPIO_Mode_TypeDef GPIO_Mode (选择模式); y5 {1 J- o5 a `& I7 H" q
其中这个 GPIO_Mode 可以替换成下面这些
GPIO_MODE_IN_FL_NO_IT = (uint8_t)0x00, /*!< Input floating, no external interrupt */8 j8 {: [. Z0 K# S' w, j
GPIO_MODE_IN_PU_NO_IT = (uint8_t)0x40, /*!< Input pull-up, no external interrupt */ I# L2 A' Y3 z& g4 D( v
GPIO_MODE_IN_FL_IT = (uint8_t)0x20, /*!< Input floating, external interrupt */
GPIO_MODE_IN_PU_IT = (uint8_t)0x60, /*!< Input pull-up, external interrupt */
GPIO_MODE_OUT_OD_LOW_FAST = (uint8_t)0xA0, /*!< Output open-drain, low level, 10MHz */& u1 H( m( \0 H3 T7 I e6 T, K
GPIO_MODE_OUT_PP_LOW_FAST = (uint8_t)0xE0, /*!< Output push-pull, low level, 10MHz */6 H+ ~; I& Z c. F9 F
GPIO_MODE_OUT_OD_LOW_SLOW = (uint8_t)0x80, /*!< Output open-drain, low level, 2MHz */8 }* v# Y$ X$ D7 B0 d% F
GPIO_MODE_OUT_PP_LOW_SLOW = (uint8_t)0xC0, /*!< Output push-pull, low level, 2MHz */
GPIO_MODE_OUT_OD_HIZ_FAST = (uint8_t)0xB0, /*!< Output open-drain, high-impedance level,10MHz */
GPIO_MODE_OUT_PP_HIGH_FAST = (uint8_t)0xF0, /*!< Output push-pull, high level, 10MHz */+ m- I4 m* o+ l, l& \9 Z. y
GPIO_MODE_OUT_OD_HIZ_SLOW = (uint8_t)0x90, /*!< Output open-drain, high-impedance level, 2MHz */
GPIO_MODE_OUT_PP_HIGH_SLOW = (uint8_t)0xD0 /*!< Output push-pull, high level, 2MHz
* m! k) b* Y/ L9 ?
GPIO_TypeDef 是一个结构体类型 ,它的定义如下: (这个对于初学单片机的同学可以不用关心)
typedef struct GPIO_struct! o) E4 [6 I7 E
{
__IO uint8_t ODR; // 数据输出寄存器( I; d' g3 w) {9 k
__IO uint8_t IDR; //数据输入寄存器$ q3 A5 Q0 S" e
__IO uint8_t DDR; //端口方向寄存器 . z/ F9 K! M4 p+ _; ~0 T& t( ?1 C
__IO uint8_t CR1; //控制寄存器1
__IO uint8_t CR2; //控制寄存器22 L) w/ f$ u, v7 { Z
}
GPIO_TypeDef;
. e2 G/ X9 X+ P0 L5 }
#define GPIOA ((GPIO_TypeDef *) GPIOA_BaseAddress) //这里是为从 GPIOA_BaseAddress 到 GPIOA_BaseAddress + 5 这段地址 //取个名字叫GPIOA ,STM8所的寄存器都是差不多这样定义的
#define GPIOB ((GPIO_TypeDef *) GPIOB_BaseAddress). R; E$ }# Y8 x' ]) a
#define GPIOC ((GPIO_TypeDef *) GPIOC_BaseAddress)9 ]) U; i) i: l2 G# J- {
#define GPIOD ((GPIO_TypeDef *) GPIOD_BaseAddress)
#define GPIOE ((GPIO_TypeDef *) GPIOE_BaseAddress)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BaseAddress)4 }2 W7 b# }) T* w2 [" S
" N6 [! \" o7 A" S E! n1 C g
9 p/ x7 m$ |# i/ m' d
) @. o# d1 D" }% W6 u6 H
( ?" p1 t0 c0 M3 W' ^1 [
1 w- Q1 D) z# U# N' q
; S/ b9 L) a8 H6 Y4 T0 z
0 u& W: [, V Q; P+ a( l5 R; R& z
# v) G8 w" K) c
& X) J* L' l4 T
LED.rar
2015-1-8 21:46 上传
点击文件名下载附件
351.84 KB, 下载次数: 70
示ä¾ç¨åº
2-å¼åç¯å¢æå»º.pdf
2015-1-8 21:54 上传
点击文件名下载附件
614.44 KB, 下载次数: 110
æå»ºå¼åç¯å¢
3-å¦ä½ä½¿ç¨åºæ¥å建èªå·±çå·¥ç¨.pdf
2015-1-8 21:54 上传
点击文件名下载附件
809.16 KB, 下载次数: 127
å»ºå·¥ç¨æ¨¡æ¿
5-LED(GPIO).pdf
2015-1-8 22:22 上传
点击文件名下载附件
500.56 KB, 下载次数: 75
é£é©°çæç¨
基于STM8S的外部I/O 中断 红外遥控解码
基于STM8S的红外遥控功能。我给大家分享一段代码。由于是产品了,不上传全部程序了* D; _3 Z& W0 {$ ]
FuncState InfraredWate_Flag; //等待按键松开标志8 w; k- W: ]4 v6 n. n0 m; Q
Uint8 InfraredWate_Index; //用于等待按键松开计时0 W4 g' w( X5 o
FuncState InfraredRec_Flag; //红外采集完成标志" e. H3 J$ v/ u" H" H; l8 g
Uint8 InfraredCode; //红外接收代码
Uint8 InfraredCode_Index; //红外接位计数( a9 q# m% S$ Y% |3 ?1 u
Uint8 InfraredBuffer_H; //红外识别码接收缓存) d) K% p) }/ h# o. I8 w
Uint8 InfraredBuffer_L; //红外接收代码缓存" L* b! @* T0 i( C6 V8 w& c3 w" R6 Q
Uint8 InfPeriod_Status; //红外周期状态
Uint16 TimeCounterVal; //计数器临时值& o7 W1 Q# E1 J' R. D
Uint8 ReciveIndex; //红外接收计数
4 k! _+ p& [3 X k& x9 m3 f2 e
static void TimeCounter_Start(void) //启动计数" `$ @6 r& M7 z" f8 l
{3 K: g) {6 k, T' v, y u
TIM2->CNTRH = 0;) j8 D% W$ s4 _( T& d. N
TIM2->CNTRL = 1;& b5 f: I5 U3 J! G/ m! t3 ]
TIM2->CR1 |= 0x01;# B0 _6 x% t7 G- }8 u
}, c, u- ^( Q: r5 F6 h
void TimeCounter_Stop(void) //停止计数
{
TIM2->CR1 &= 0xfe;9 w+ e$ x6 y+ H/ _3 @: V
}) U. c5 z1 a& O2 ~( q5 P
; [2 n; Z& @7 L# y9 [) A
static void InfraredRec_Bit(Uint8* BufferAddr) g4 _/ C/ e# E. z- ]% v
{2 e h8 p9 K5 s1 S5 A e x
if((12000 < TimeCounterVal)&&(TimeCounterVal < 22000)) //0周期约为17000
{6 L* ?1 S. k1 c( ]8 b- c
*BufferAddr = (*BufferAddr) * 2;
}
else if((30000 < TimeCounterVal)&&(TimeCounterVal < 40000)) //1周期约为35000
{
*BufferAddr = ((*BufferAddr) * 2) + 1;
}
else ReciveIndex = 0;
}
void InfraredInterrupt_Process(void)
{2 n7 u/ q4 t8 a/ _6 Y. k+ f
if(InfraredWate_Flag != L_FASE) //等待接收到按键松开的间隔符,才开始下次接收 每次中断4ms,10次约40ms2 d# K2 V. }/ }
{8 ^$ A* d9 s1 g% H! N+ p
if(InfraredWate_Index <10) InfraredWate_Index ++;
else
{ E! E! S6 i# P! A8 f/ z4 [
InfraredWate_Flag = L_FASE; //已等到间隔符,清标志位
TimeCounter_Stop();
InfPeriod_Status = InfPeriod_Start;( h" E. m) s& ~4 s4 q
}
}
else //防止计数中间产生的,错把间隔符当成正常位。还有一种是由于排除红外不工作时产生的干扰。1 D, l: R7 s V1 @5 I
{4 y. U+ o, F' W6 s4 n1 ? j
TimeCounter_Stop();. K9 u, Y' U3 }0 t H: F# ^
InfPeriod_Status = InfPeriod_Start;5 b' `! z/ V }! C( Z2 [3 q% [/ A
}, N: [! D/ @1 S
}% J$ f; V( Y* L' \7 s
void PeripheralInfrared_Receive(void)2 C2 N9 x5 P# r5 h8 h! [) P& T
{
if(InfraredWate_Flag == L_FASE) //处于非等待间隔符状态,开始正常接收 " L7 E# g" a9 F% v
{. J/ w& v- U N
if(InfPeriod_Status == InfPeriod_Stop) //开始接收数据
{
disableInterrupts();, p1 A n: m# P; `+ @7 d1 E
TimeCounterVal = ((uint16_t)TIM2->CNTRH << 8);
TimeCounterVal |= (uint16_t)(TIM2->CNTRL);
TimeCounter_Start();
enableInterrupts(); //存在一些硬件上的互锁机制,为了使得数据更准确,这里关掉中断,防止其它干扰% T4 y1 {% M/ c& ^- B' x
ReciveIndex ++;
if(ReciveIndex <= 8) InfraredRec_Bit(&InfraredBuffer_H); //前8位数据+ e! v: N, M; g/ O2 `
else InfraredRec_Bit(&InfraredBuffer_L); //后8位数据 # _( M5 [, L- c/ k7 c+ q
if(ReciveIndex >= 16)6 q! M0 o. I; ~6 [# Z- D. r) u
{& H! _, t9 p9 V' J- b; z" H' Y
InfPeriod_Status = InfPeriod_Start;& s& r( R! k& _3 C. e
if(InfraredBuffer_H == InfraredUser_Code) //如果识别码正确# h$ n1 g5 t3 c' Z% m
{4 U. d- d2 b4 n0 @
InfraredWate_Flag = L_TRUE; //已接收到功能码,等待间隔符
InfraredWate_Index = 0;
InfraredRec_Flag = L_TRUE; //5 j, n, x" P+ a5 F/ S
InfraredCode = InfraredBuffer_L; // 将数据送入缓存区
}
} ! N4 m: v; e% k8 Z! K' V$ V
}
else //如果接收到了启始符,置标志位,开始接收数据
{& |3 H# z% G3 |9 I) n' L; |
TimeCounter_Start();4 e" O( o- u' w7 }
InfPeriod_Status = InfPeriod_Stop;
ReciveIndex = 0;
}' p8 R. X5 ~" A- [% z
: p3 M9 B# d( r1 t7 H0 c
}
else InfraredWate_Index = 0; //在等待按键松开,理论上讲,是不允许再接收重复红外信号的
}https://www.stmcu.org.cn/module/foru ... &pid=2049512&page=12 J$ e8 q; ?, a; q
void Infrared_InitVar(void)
{
InfPeriod_Status = InfPeriod_Start;
TimeCounterVal = 0;
ReciveIndex = 0;
InfraredBuffer_H = 0;
InfraredBuffer_L = 0;
InfraredCode = 0;
InfraredCode_Index = 0;8 k$ u6 y# q. v+ }1 \: G$ u1 N
InfraredRec_Flag = L_FASE;
InfraredWate_Flag = L_FASE;
InfraredWate_Index = 0;' _ C3 N2 W( B3 \; q$ C) I
}; D* H# a) G' h& l# D/ l
3 c9 x1 e+ h5 ~: Q3 C& Y
在这个红外编码中由16位组成一串字帧。0 由 0.26ms的低电平和0.8ms的高电平表示, 1由 0.26ms的低电平和1.8的高电平表示。3 k' e2 ?& j, j f$ Z, ]
两者的周期相差一倍,我把红外信号由红外接收头HS3818接收后送到STM8S的外部中断I/O上面,我是根据数据周期来判断是0% z2 c# n. F; C7 `' _: D
还是1的。( M% ~, Q# x0 _: |
STM8S按键驱动
按键分为独立按键和矩阵按键,矩阵按要比独立按键省I/O口,但是驱动程序要复杂。这两者各种有利有弊,那么有没有两者兼得的方法呢。如果的你的工程中有段式LED的话,恭喜你中奖了,看看我的方法吧。
从我的测线板的原理图中可以看出,我在不增加I/O的情况下,让我的测线板多上了8个独立按键。那么如何驱动这8个独立按键呢。以前学
的都是,先判断到有按键按下,再等10ms,如果按键还是按下的,则置下按键按下标志。那么问题来了,10ms的延时(不符合节约的原则),还有
如果我的按是长按的怎么处理呢。我分享下我的方法。我也是在中断中处理的。
我会为什么时候执行显示,什么时候处理按键检测 排个次序。有点类似操作系统中的分时复用。现在把执行按检测的部分独立起来看。
每次执行按键检测的时候,我会先判断8个按键的状态,看看有没有被按下,如果没有,则不用理会。如果有,则记下一标记,下一次时中断,还5 @, Z0 b6 R" S g2 ]
有按下,那好了,程序就认定有按键按下了,接下来的连续几次进中断,还是有按下,则认定它为长按了。我附上我的程序。/ o- |) W" S9 j6 s6 Z/ o' ^( Z o7 f7 A
5 v0 U' q# N" r* q$ q* T3 L/ A
KEYStatusDef KEYStatus; //按键的状态 分为三种: 没有按下; 已经按下了,等待下一个中断确认; 等待按键松开,或确认为长按
KEYValueDef KEY_Value;& J2 b6 A. m: J6 ]% n
FunctionStatus KEY_Flag;
static Uint8 KEY_Temp1;
static Uint8 KEY_Temp2; ! m1 O, T( ^9 C1 i# O
static void KEY_PortScan(void) // 从没有按键按下到按键的确认
{
KEY_Temp1 = KEY_Data;
if(KEY_Temp1 != 0xff) / r7 k1 _/ J. j" x6 J2 k9 o7 I7 A
{ % H3 {4 {6 C; v1 E& m. l+ H4 }! o
KEYStatus = KEYStatus_Ok;5 t$ L& e$ m9 w5 `1 N9 Q7 q0 {
}5 J0 b: I4 {8 S& z
}
static void KEY_PortOk(void) //从确认按键下到等待按键的松开. w! t3 o; h2 F- R1 Y) R
{
KEY_Temp2 = KEY_Data;: K; Z, |' X7 D9 L' g8 c
if((KEY_Temp2 == KEY_Temp1)&&(KEY_Temp2 != 0xff)) //再次判断按键,如果值和上次相同,则认定有按键按下' t4 E/ [+ j9 {9 C5 G' w
{
KEY_Value = (KEYValueDef)KEY_Temp2; //保存按键值
KEYStatus = KEYStatus_Pro;
KEY_Flag = L_TRUE; //置按键按下标志位1 ~( x. e/ L8 F) i" G/ G- M
}else KEYStatus = KEYStatus_Scan; 5 n, J& f( q5 i, t! O- r
}9 B) @7 K& t1 q8 E3 {
static void KEY_PortPro(void) //判断按键的松开,去检测下一个按键的按下7 y4 F, J9 H0 Q
{
KEY_Temp1 = KEY_Data;) j+ [! z* N) w, D5 u" z
if(KEY_Temp1 == 0xff) KEYStatus = KEYStatus_Scan;5 I0 R, X+ j1 G# ]
else /*判断按键 长按*/0 h$ w% \% D V) Q' o6 Z: S
}4 u- f3 i8 L) s/ h% g
static void KEY_Scan(void)8 Z. \/ A. v* v: ~) x6 v; f
{3 [9 a4 V' K8 B( R4 S( N, a" s
KEY_Input(); // 置I/O为输入端口,用来检测按键输入功能( @& T4 \+ {* F. t8 k: z
//在主频比较大时,内核的处理速度大于I/O的处理速度,因为I/O最大输出速度只有10M,输入更低。STM8S最高主频有24M,这里加空操作,数量按实//际测试而定,我为工程的稳定性,很保留的加了10个nop();,5个也可以了。
#if UserSystemClock > 6
nop(),nop(),nop(),nop(),nop(),nop(),nop(),nop(),nop(),nop();
#endif
if(KEYStatus == KEYStatus_Pro) KEY_PortPro(); //有按键按下,等待按键松开或按键长按认定% C$ T+ x7 {; y8 F$ N# p* U
else if(KEYStatus == KEYStatus_Ok) KEY_PortOk(); //确认按键按下( S( R( } b' \4 V3 F- F
else KEY_PortScan(); //扫描有没有按键按下, 这一个放在最后,用 else,也是为了让程序具有自动修复能力
KEY_Output(); //置I/O为输出端口,用于显示功能6 ?+ c& o9 w3 O- m( h
}
8 n) |( [+ e+ {. @/ M2 J8 M
void KEY_Varible_Init(void) //初始化按键相关变量的初始值.
{; y* J$ f' z4 [* ^5 e; q
KEYStatus = KEYStatus_Scan; 3 K+ ^/ C& G3 H) R; e
KEY_Value = KEYVal_NO;) A! L5 C6 ]7 |
KEY_Temp1 = 0xff;
KEY_Temp2 = 0xff;- k# L' Z0 G* e, _- u5 w
KEY_Flag = L_FASE;5 D1 u% j5 d0 n3 C1 @; Z3 J) k
}- F5 O) [0 k1 ]& [
: u U6 R. e4 [0 @# N
上面的介绍只是代码部分,硬件部分有个地方大家要注意,就是那个按键公共端上面接了一个电阻R10。它取值很重要,为了防止在按键按下时,数码管对应的段不亮。综合下面的两点,我的板子上R10实际取的是2K;3 f3 D/ u8 V- w8 E
1 参考STM8SR207RB数据手册,STM8认定0.3VDD为低电平,保守一点,最好不要超过0.15倍VDD。而按的上接电阻是33K,其实有点大了,1 T) S6 F- W7 G, m4 v' a& j
我实际焊板测试后,选取的是22K,因为这直接影响了前面判断按键状态的准确性,还有一点大家不要忽视了,芯片内部还有个上接电阻,% x2 A+ S0 `% R0 I% Q' M5 L1 F& S: W
根据数据手册 上接电阻为30~80K.
2 数码管点亮它有一个压降(大家不要认数码管也是0.7V),大多数码管是2~3V的样了。所以数码管与单片机连接处加一个限流电阻,这个电
阻是多少?
a,数码管点亮2ma足够了。(5 - 3)/ 2 = 1K ,尽可能的减小这个电流,单片机驱动能力是有限的
b , I/O输出的5V分在限流电阻和R10上,保证R10的电压大于3V。
A! l8 g" Q5 s. B* X
端å£çµæ°ç¹æ§
) r9 u9 @2 J0 |. R2 \$ A8 O& H- G
我的板子是我自己做的,上面有一个四位共阴数码管,8个LED和8个按键,一个蜂鸣器,一组测12根排线通断的接线口,一组用RS485芯片引出来的串口。我的板子的原理图和PCB我会在附件中提供。想要学习好STM8S单片机,大家也最好能有一块开发板,淘宝上卖的很便宜,少吃点香蕉就可以买到了,卖家还提供有很多的例程,个人建议大家买个简单的板子,有条件的话,建议自己做一个。
目前STM8S的编程软件有 IAR FOR STM8 和官方的STVD ,个人强列推荐IAR FOR STM8,STVD比较难用,我两者都用过,现在一直在用 IAR FOR STM8 1.3(这个软件大家自己网上搜了,比超市里的香蕉还要多)
我的板子用的芯片是STM8S207RB ,对应的数据手册和参考手册在这里直接下载。(https://www.stmcu.org.cn/document/list/index/category-17 ),第一个和第二个就是(RM0016 参考手册 和 DS5839 数据手册)。数据手册是介绍该芯片里面有哪些资源、片上外设供大家使用,以及它的电气参数(相当于超市里面挂在香蕉前面的牌子)。而参考手册刚是介绍如使用编程这款芯片了(其实这款芯片毕竟是8位单片机,入门也相对比较简单,祝大家早日能像吃香蕉一样简单的用它)。我的原理图和PCB是用 altium designer 9.3画的。
还有个官方的STM8S /标准外围库: https://www.stmcu.org.cn/document/li ... ew/category-504?p=2 (倒数第二个STSW-STM8069)
如果你用到的芯片也是STM8S207RB的话,直接用我的附件的中的 工程模板就可以了。代码库中有帮助文挡和官方例程。
最后还有个福利,标准外围库,中文函数生成器,在附件中(STM8S真是好,这也能有! ! ! 被天上的馅饼砸中了,满满的都是幸福! ! !)
æµçº¿æ¿åçå¾.pdf
2015-1-8 20:52 上传
点击文件名下载附件
507.07 KB, 下载次数: 228
åçå¾
æµçº¿æ¿PCB.rar
2015-1-8 20:52 上传
点击文件名下载附件
519.81 KB, 下载次数: 174
PCB
STM8Så·¥ç¨æ¨¡æ¿.rar
2015-1-8 20:58 上传
点击文件名下载附件
348.53 KB, 下载次数: 250
å·¥ç¨æ¨¡æ¿
stm8slib.rar
2015-1-8 21:01 上传
点击文件名下载附件
80.76 KB, 下载次数: 186
同问
是不是类似网线那种, 通断显示
是这样的一个功能。
STM8s定时器的使用程序
STM8S包含有三种定时器,高级定时器,通用定时器和基本定时器,这三种定时器在功能上是包含关系的。在这里做一段最简单的1ms的定时功能。5 F/ l5 g. r) q
static void MCU_TIM2_Init(void) //定时器2 1ms中断初始化0 M3 U8 b8 @# ]# J) g$ S
{6 j: p. h! O, H8 f# d( Z
TIM2_DeInit(); //复位所有寄存器值,这一步呢可选,为了产品稳定性,最好还是选上
TIM2_TimeBaseInit(TIM2_PRESCALER_1, 0x07d0); //2M 内部时钟,一分频 2000
TIM2_UpdateDisableConfig(DISABLE); //允许更新 即是在每一次定时器溢出之后,会自动更新计数器
TIM2_UpdateRequestConfig(TIM2_UPDATESOURCE_REGULAR); //只允许规则请求更新,即是只允许定时器溢出更新
TIM2_ITConfig(TIM2_IT_UPDATE, ENABLE); //打开定时器溢出中断
TIM2_Cmd(ENABLE); //启动定时器8 N0 x) r8 O# [' X1 R
}
通过上面的一段程序即实现1ms 的定时功能 :我的这个示例是在默认的系统时钟,2M / 1000 = 2000 ,所以模寄存器的值0x07d0。大家是别的主频的' k+ Y2 q M m! i4 F& b7 i6 |
话,根据想要定时的周期,对应的调相关系数即可。有库函数用起来真的很简单。我再上传一个风驰大哥的教程。
有了定时器的中断,必有中段函数,库模板已在库函数 stm8s_it.c中写好了
INTERRUPT_HANDLER(TIM2_UPD_OVF_BRK_IRQHandler, 13) //定时器2溢出中断
{
/* 用户函数*/
TIM2_ClearITPendingBit(TIM2_IT_UPDATE); //清除定时器2溢出中断; p0 i. ~8 B$ T: X
}8 u$ [# o$ T$ n+ q
# w4 q4 o# K+ A4 J; s1 n
; \9 \0 a! `& d/ m0 ~+ F1 z. R
10-TIM1(宿¶).pdf
2015-1-9 09:30 上传
点击文件名下载附件
428.3 KB, 下载次数: 73
TM1宿¶
数码管驱动
2 ?$ T) {5 U/ ?, I$ @: t. ^) F
数码管驱动分为动态驱动和静态驱动。这里只讲我的动态驱动的设计方案供大家参考
我以前上学时老师教的动态驱动是这样的。
void main(void)* q% ?1 ? k& Z) p
{7 q; b: k6 u- \
while(1)
{
display_ONE(); //第一位数码管点亮
delay_MS(10); //延时个10ms
display_TWO(); //第一位数码管点亮
delay_MS(10);
display_THR (); //第一位数码管点亮
delay_MS(10);
}! x, ^3 x: K5 ^! H3 R8 u6 H% D/ h
}
这样的写法只适用学习如何动态显示数码管,他有两个不足的地方,一个是延时函数浪费了很多处时器时间,另一个是如果这个循环中再加上别的任务,随着工程量的加大,数码管就会越来越暗,有时候甚至不亮了。那么怎么办呢,那就得要基于动态显示的原理进行改进了。在上一段中,介绍了定时器,我分享下我的方法。我是把 这段程序放在中断中。& U D% `: a6 m& C
INTERRUPT_HANDLER(TIM2_UPD_OVF_BRK_IRQHandler, 13)
{7 {+ c) e& A( v* L
switch(Dispay_Index)
{
case 0: LED4_Disable();Dispay_Index = 1;LED_Data=LED_Buff[4];LED0_Enable();break; //LED指示灯4 i9 p$ M0 X7 W! \. r: P: S' n
case 1: LED0_Disable();Dispay_Index = 2;LED_Data=LED_Buff[0];LED1_Enable();break; //第一位数码
case 2: LED1_Disable();Dispay_Index = 3;LED_Data=LED_Buff[1];LED2_Enable();break; //第二位数码
case 3: LED2_Disable();Dispay_Index = 4;LED_Data=LED_Buff[2];LED3_Enable();break; //第三位数码
case 4: LED3_Disable();Dispay_Index = 5;LED_Data=LED_Buff[3];LED4_Enable();break; //第四位数码
default : LED4_Disable();Dispay_Index = 0;KEY_Scan();break; //扫描按键) ~/ n+ Y9 P0 B8 I) k
}; P( X7 Z# x) [6 }/ b5 [, P
}$ m2 ^2 n7 B. G9 n3 I# N& D) z$ \% S. f
我的这段程序是这样的一个功能,每次进中断,更换显示一位数码管。在中断中可使数码管的显示与工程的主任务独立起来。不管其它地方如何变化,. ]- ?( ^) d" o
数码管的显示总会是稳定的。Dispay_Index 它是一个全局变量。如果临时变量的话,在退出中断时它的值就消失了。最后default建议大家一定要使( e# a! w' ]- A6 v1 G2 r; p9 {
用,因为它使得这个显示程序具有程序跑飞时的自我修正能力。由于我的LED和数码管是共用段码口的。所以我把LED就当作一位数码管来看待了。; L! t' ~, ]7 z+ F7 N( {5 T! ]
我的按键也是共用端口的,所以我也是把它当作一位数码管来看待的,只不过数码管是输出,它是输入而已。下一段我和大家分享一下我的按键驱动
方法2 a& {& D4 V# m/ i5 n1 k3 i
8 @# u: R4 ]0 |. T+ h9 a7 t
* b& J5 p) M! S0 ^6 n& \9 p# T
这一节介绍下如何基于库函数的头文件让STM8S支持位操作
在使用51单片机中,我们可以很方便的定义位 sbit LED_0 = P0^1;
但STM8S库和IAR并不支持这样的操作,这给我们在写应用代码时带来了很多不便。那我们只能对面STM8S的I/O寄存器进行重新封装了。% Q: d3 u- d/ h
在正式写程序时,我们要了解结构体在内存分配时,内存是连续不断的一片。 结构体和联合体部分不太理解的同学大家网找找。
先看库是如何封装I/O寄存器的。0 ?- F3 w9 E M: N- ~3 e# V' H
typedef struct GPIO_struct //定义I/O结构体类型' ^$ L8 |" u1 D
{; }& w! b/ C. e. x }
__IO uint8_t ODR; //!< Output Data Register - D; O1 \2 J7 F! k+ O
__IO uint8_t IDR; //!< Input Data Register 3 ?. y; Q: y( Q2 J6 R' S
__IO uint8_t DDR; //!< Data Direction Register 4 X# r: B- d$ ` M5 ~% A* R6 |+ t
__IO uint8_t CR1; //!< Configuration Register 1
__IO uint8_t CR2; //!< Configuration Register 2
}% t' x3 n* p, h: [
GPIO_TypeDef;
* v, M/ V- _/ L7 O1 [5 L
#define GPIOA ((GPIO_TypeDef *) GPIOA_BaseAddress) //强制定义端口名& }2 _' @ w. D; o! N
这样的话,我们想往 GPIOA上送数据,只要这样即可:
GPIO->ODR = 0x01; //即可轻松的为 GPIOA端口赋值了
% q7 d h- ~, d# |8 ]
我们要如何封装呢。这是我现在用的。' n3 d5 q. Z2 C0 F2 J" K: R: b
typedef struct GPIOBIT_struct //重新定义端口 因为一般只要用到数据输入和输出即可,我只定义了 ODR 和 IDR5 ^ F5 b r. r: O4 g! P! @! l
{
union
{7 G! G; b) r+ Z: a( z* j9 S. Y. X1 G
__IO uint8_t All;
struct
{3 o7 A6 r1 M& h+ @1 N
__IO uint8_t B0:1;3 ~! Q9 s3 O N# E) B. Z
__IO uint8_t B1:1;+ e' q" ?+ t8 K. |
__IO uint8_t B2:1;
__IO uint8_t B3:1;
__IO uint8_t B4:1;' ~& e7 k5 S( g2 Y R: G
__IO uint8_t B5:1;
__IO uint8_t B6:1;
__IO uint8_t B7:1;& X0 L2 I7 {4 a+ t) T3 Z
}Bits;9 n0 K$ Q6 h8 T/ r1 }* B
}ODR,IDR;( u6 k: f$ ]. C1 |/ D
}GPIOBIT_TypeDef;3 ~, D5 F: P4 i' }# A" O$ |4 J
#define GPIOANEW (*((GPIOBIT_TypeDef *) GPIOA_BaseAddress))) V0 ^: L( l) i
( j% f3 w7 F( J: E; ^8 i+ ?; `0 C
这样将GPIOA重新命名为 GPIOANEW 现在往GPIOA口输出数据就可以样写了
GPIOANEW->ODR.All = 0x01;
单独往第1位写如何写呢? 如下即可。 z8 e+ p& H4 \8 z. ]
GPIOANEW->ODR.Bits.B0 = 1 * I! } ^ q; Q2 V6 h9 B4 m
. i7 g$ d. k9 K+ x% f; o+ C
这样写是不是太麻烦呢,我们再封装它
#define GPIOA_0 GPIOANEW->ODR.Bits.B1& l D& D, k# c% `& _/ H. J
GPIOA_0 = 1;/ g- O' q8 ?7 Y2 C" [8 b7 G. q X
关于它的位操作这么多也差不多够用了,其它大家自己多思考了
& o# L2 E, V6 }9 N" ]
% |+ u7 u; R8 @8 e
接下来就是介绍如何进行测线了。
在测线程序设计之前,还是老样子,先想好这个用什么方法去测。2 q5 I1 h0 M& C& t- @9 N! w, B
在大批量生产排线的时候,一般会出现如下三种问题3 c+ e4 |7 x1 _* h
1. 线断了 2. 线交叉了 3.可能存在接触不良的情况2 ]( `4 e6 Z8 R) b3 s
如果用人眼去识别哪些线不好,哪些是好的。几根好办,几千条线呢,几万条怎么办呢。 只能借助自动化设备了。, K' J' }) Q" b6 o/ @+ F% D
6 v0 f- Y. ^& q6 ~6 M- h' S* M2 E- R
我呢提供一个我的思路。我还是放在中断当中去做。7 v Z( f9 c8 W6 C+ y3 w( Z
第1次进中断,我给第1根线送一个低电平,再在线的另一端去检测它,将这个值保存在变量的第1位中,再将第1个端口复位为高电平
第2次进中断,我给第2根线送一个低电平,再在线的另一端去检测它,将这个值保存在变量的第2位中,再将第2个端口复位为高电平- \% U5 M! r' G. h* M, U
...
...) k$ _0 k* v2 G6 ]4 r3 e' [
第12次进中断,我给第12根线送一个低电平,再在线的另一端去检测它,将这个值保存在变量的第12位中,再将第12个端口复位为高电平. R7 U% E9 L8 h# i6 u1 N+ x
测完了12根线后。我判断这12根的状态,并保留下来。作一次测线计数。
我们知道是线插上插座和拔下插座的过程中,这个状态是不稳定的。为了避免这个情况的误判, 我每次在测完12根线的循环之后,都记下它的
状态,直到10次连续测量的状态都是一样的,才最终认定这根线是好还是不好。7 e1 G% I% z; t9 e
1. 对于好的线,在数码管上面显示一个 PASS 表示通过
2. 那么对于不好的呢,找出是哪一根线不好,并把它在数码管上显示出来,让检测人员贴上标签,便于产线上工作人员维修
如何算快速算这个线是哪一根呢?方法有很多种,1 B( Y! p1 Q1 x: q3 Y' l
我用的是杳表法。把UCOSII当中 找最高优先级的表给复制过来就可以了。2 b. x2 ~ W; l" F3 q
是不是这样就算完了呢,为了使我们做出来的东西更像一个产品,我们还要给它润色。$ {6 Z" k, M$ V- f9 A+ m5 Q
1. 我还会让产品计下测量多少个排线,产品的合格率是多少?
2. 将最近10次的产品测量数据进行对比,看看产品的生产效率和合格的率情况, 便于提高生产 (STM8S有内部EEPROM,这些数必须保存在其中)
3. 根据其它产品的需要,我测的线肯定能只是12根的。 只要不超过12根,我们可以通过这个菜单去设定的% j" H" x: t2 S+ q, o4 W
4. 可以通串口将这些数据上传到电脑上,结合上位机软件,将它们制表格% F+ R, o6 z6 y- u! q8 ?) o
如何去写这个菜单的结构,其实也有很多东西,大家可以在别处杳查。在8位机写一个很大的菜单,而且要有高的处理速度,我用是的结构体去做的,
也是在网上的一位大哥的贴子上学的,可如今找不到连接了,它可以让一个10 x10 的菜单执行速度 和 1X 1速度一样快。/ _3 ^7 S* q& W: v+ w/ j
( X/ Z1 l# {9 } V5 f
这个测线板本来是想写成教大家入门STM8S的,我不常写这一类文档。由于工作上比较忙,写着写着就变了。我更多的是写出了我平常工作中总结
出来的比较好的程序设计思想。我把我的程序也上传过上来。4 f$ O4 h" H! L* l
个人写文档能力差,希望能有写文档能力强的朋友指导指导。俗话说: 会写画板子,会写程序的叫技术员; 会写报告、会做计划的是经理; 会做策划,会做方案的是老板。
祝我们这些一心想学想做技术的朋友们早是成为老板!!!!
; H0 N. g2 ?) T1 c
( N$ B# m* |; A2 j
( v2 Z8 y {7 R5 g# V0 o
' l. j: H, o# C: Z
æµçº¿æ¿ç¨åº.rar
2015-1-9 12:46 上传
点击文件名下载附件
413.18 KB, 下载次数: 55
æµçº¿æ¿ç¨åº
基于UCOSII 的测线板程序
如果家能看我的贴子做出了这个测线板,那么STM8S的应用也就没有什么问题了。我在上传一基于UCOSII2.5的测线板的程序,让大家学一学UCOSII 的使用 和如何把UCOSII 移植到STM8S上面,移植我参考的也是风驰大哥的教程,他移植的UCOSII 2.9 ,我自己在学完教程后移植的是UCOSII 2.52版的。我讲一点个人对写程序感想吧。写程序最重要的程序的结构,写程序的代码风格。其实学会用一个单片机并不难,像STM8S买一块开发板,对着入门教程很快就会了,我入门大概花了两周的时间吧。(这里我真心感谢风驰大哥,他把他的教程免费共享出来,在我心中他和STM32的原子哥都非常值得我们去尊敬,他们把他们的东西免费共享给了很多利益不相干的人,包括他们的竟争对手,这不是一般人能做到的)
åºäºUCOSIIçæµçº¿æ¿.rar
2015-1-9 12:50 上传
点击文件名下载附件
478.04 KB, 下载次数: 42
关于STM8S看门狗
关于STM8S的看门狗,有一点非常要注意,它这个和我正常写看门狗不太一样。它要先开启看门狗,然后才能对看门狗进行配置,这一点官方的参考手册上标的不太明确
STM8S的看门狗应该是这样的顺序8 C. d- B+ B( m1 @6 F5 A# x# Y
1 [% e; o# a0 l! F2 F/ S
static void MCU_IWOG_Init(void) //看门狗初始化,只允许写一次
{
IWDG_Enable(); //开看门狗1 c8 l9 ]4 z8 N: _
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //写允许, I2 D# @- [" Y% _3 D F6 N$ J2 y
IWDG_SetPrescaler(IWDG_Prescaler_256); //256分频' V& o8 @" w0 ~/ G- O
IWDG_SetReload(0xfe); //254的重载值1 p1 y6 M1 ?6 Y, |6 S( }7 K u
IWDG_ReloadCounter(); //刷新计数) j$ [0 L h9 I/ G5 |; J
}