2024年1月21日发(作者:)

网站上整理的免费分享说实话我能够使用的单片机不多,我总是以为无论什么单片机都能开发出好的产品。前些年用51,总是向各位大大学习,无休止的索取,在网上狂览一通。心里感激的同时也想奉献一些,可是我会什么?后来使用avr(公司要求)还是向大大们学习,我又想奉献,可是我会什么?我会的大大们都写了,我不会的大大们也写了。一个星期前花项目经费买了***的kit三合一板,最近几天闲了下来,便动手调试一下。算是有点心得,我又想奉献,可是我会什么?我只是想和大大们交流一下,哪怕是对的或者是错的,大大们满足我的一点心愿吧。唠叨了这么多,现在开始吧。配置:stvd,cosmic我学单片机开门三砖总是要砸的。第一砖:电源系统,这没什么好说的,只是它是stm8工作的基础总是要提一下第二砖:时钟系统,这等下再说。第三砖:复位系统,stm8只需要一只104电容从reset脚到地就可以了。现在说说时钟系统,学习单片机无论8位的还是32位的,都要从时钟开始,下面是我一开始的时钟切换程序。1234CLK_ECKR|=0X1;//开启外部时钟while(!(CLK_ECKR&0X2));//等待外部时钟rdyCLK_CKDIVR&=0XF8;CLK_SWR=0XB4;//CPU无分频//选择外部时钟5CLK_SWCR|=0X2;//使能外部时钟上面的代码看起来没什么问题,可在调试过程中出现了有时能切换,有时有不能的情况,后来发现只要在第5行设上断点就能切换,我就想是不是得让cpu等一下,我又仔细的翻看下rm0016的时钟部分,发现得等待CLK_SWCR的标志位置位才能切换。就变成了下面的代码

网站上整理的免费分享CLK_ECKR|=0X1;//开启外部时钟while(!(CLK_ECKR&0X2));//等待外部时钟rdyCLK_CKDIVR&=0XF8;CLK_SWR=0XB4;//CPU无分频//选择外部时钟while(!(CLK_SWCR&0X8));//这里要等CLK_SWCR|=0X2;//使能外部时钟现在一切ok,是不是觉得看东西要仔细一下~~。顺便说一下,stm8有三个时钟源的,hse是外部时钟,hsi是内部16mhz的时钟。Stm8一启动默认为内部时钟,并且8分频。其实这么处理不是最好的办法,如果外部时钟出了问题,stm8要傻傻的等待到死。它可以有中断的,在中断中处理一切,包括恢复时钟源,这才是正道,只是我比较懒,不是做正规产品,想都不愿去想。长长的一篇,没什么内容,请原谅我的唠叨吧。又想起一句,仔细看手册里的时钟概略图吧,这对你有帮助。第二节:傻的可爱—cosmic和time的事情使用单片机定时器总是用到的,无论是延时,键盘扫描,显示刷新,还是巨无霸的操作系统。Time1太过复杂等过些天再说,我是从time2开始的,从简单的定时开始吧。简单的解释一下,time2是向上计数的,不像time1可以双向计数(这对我很有用,我可以使用它的正交编码功能,这正是我学stm8的初衷,它可以让我省下一片正交计数器或是一片cpld,等过两天从公司借个编码器,调试一下),我们怎么可以达到定时1ms的目的哪?关键是TIM2_ARR这个寄存器,TIM2_CNTR是计数到TIM2_ARR就产生更新事件,然后清零从头开始的,看下面的代码。

网站上整理的免费分享1CLK_PCKENR1|=0X20;//开启时钟,stm8的外设时钟可控2TIM2_PSCR|=0X3;//DIV8为1us1US->外部晶振8mhz除以8实现单位时间3TIM2_IER|=0X1;//允许中断4TIM2_ARR=0X3E7;5TIM2_CR1|=0X1;//关键是这里//开启定时器这看起来没错,可就是不能实现定时效果,这是为什么?答案出乎我的意料,看汇编代码后才发现,comsic使用了ldw指令,而ldw指令是先写低位再写高位的。ARR寄存器是要求先写高位再写低位的,将第4行改为TIM2_ARRH=0X3;//TIM2_ARRL=0XE7;后,问题解决。用avr时gcc编译器都给做好了,comsic很傻很强大。记住这个教训吧,要看编译器手册,不要偷懒,多写一行就多写一行吧。中断部分以后再说。就到这里,明天再聊,这耽误我看小说的时间了,哎,为了stm8我已经4晚上没看小说了。第三节:ad的单次转换说起ad我是就头大,不是说stm8的ad让我头大,而是以前在产品中使用的ad老板总是要求越来越高,从16bit到24bit,从逐渐逼近到sigma,在电路上克服小信号的采集实在是一件痛苦的事情,至今在24bit的采集上只能到18bit有效位,有经验的朋友一定要告诉我。又扯远了,stm8只是10bit的ad,随便用用就可以了,我从来没指望它能给我出大力气,当然大大们做民品,或是别的要求不高的可以用用。为什么说单次转换呢?因为简单,因为我懒。看下面的代码吧//这里是初始化CLK_PCKENR2|=0X8;//使能adc时钟

网站上整理的免费分享ADC_CSR|=0X3;//选择通道3禁止中断//使能ADC,18分频//数据右对齐,low8BITATADC_DRL;ADC_CR1|=0X71;ADC_CR2|=0X8;//这里是转换结果unsignedintx;unsignedintx_h;ADC_CR1|=0X1;//启动转换14个时钟周期while(!(ADC_CSR&0x80));//等待转换结束ADC_CSR&=0X7F;x=ADC_DRL;x_h=ADC_DRH;x_h=(x_h<<8)+x;returnx_h;//清除中断标志//READDATA因为是右对齐所以先读低位这次没出什么错,大家失望了吧!哈哈,说点题外话,做16bit以上ad我认为要注意几点1.有一个好的基准2.传感器供电最好和基准联动3.要有效去除长线干扰,如加屏蔽网,做线阻平衡。4.使上两个好的电阻吧,会省很大力气5.布线要花大力气,不能瞎布。其它的还有很多,大大们到网上看吧,前人栽树,后人乘凉。我们即要做前人,也要做后人。第四节:中断系统和一杯热茶最近喜欢喝茶,准备去买一套茶具,一个小壶,八个小杯那种。我喜欢

网站上整理的免费分享铁观音,浓浓的,滚烫的,直入喉咙。中断就像一杯浓浓的铁观音,没有操作系统的时候,使用中断吧,一样可以达到实时响应。没有极品龙井,就喝铁观音吧,一样口齿留香。Stm8的中断是有优先级的,不是avr那种假优先级,是那种低级中断正在处理,高级中断可以终止它的优先级。我们不说这些,它在不做项目时,离我还很遥远。说说comsic的开中断手段吧看下面的语句_asm("sim");//这是关中断_asm("rim");//这是开中断我刚开始还以为sim是开中断,结果定时中断总是进不去。_asm()插入汇编行,多行可以用n分割汇编块可以使用下面格式#asm//汇编代码#endasm或者#pragma#pragmaasmendasmStvd自带了中断处理文件,在向量表里修改中断号处的函数名,来实现中断发生时程序跳到我们的中断处理程序。我写了前面关于time2的更新中断。向量表中irq13处改成这样{0x82,TIME2_UIS},/*irq13*/@far@interruptvoidTIME2_UIS(void){if(++count>temp){count=0;PD_ODR^=0X1;//LED翻转}TIM2_SR1&=0XFE;//中断标志位,它不会自动清零

网站上整理的免费分享return;}Temp是前面ad转换的结果,这里来实现led的闪烁频率。@far是指长指针,@interrupt指示这是一个中断处理函数。本来还想说uart的中断的,又一想明天我说uart的时候说啥。所以还是留在明天再说吧。茶喝的多,睡眠质量受影响啊。第五节:永恒的串口和阶段感言等说完串口,就要等一些天再和大家见面了,孩子总是和我捣乱,那是我的第一生命。是我祖祖辈辈的延续。请原谅我的古老,我喜欢传统的,无论是京剧,大鼓还是快板。说起孩子,心情总是愉快的,有一天孩子感冒去医院,医生要验血,临近化验室时,孩子哭闹,妻子哄骗说是妻子验血,等抽完孩子的血孩子哇哇大哭并质问:“为什么你化验抽我的血”我和妻子苦笑。现在想来,孩子那时天真可爱,现在的孩子俨然一副大人麽样,他才4岁呀,是我做的不好吗?我从来不让他在家做和玩耍无关的事情,包括学习。别家的孩子大都报各种专长班,我从来都阻止妻子去给孩子增加负担。我要他的童年快快乐乐。我要让他童年充满童真,可是我做不到。孩子越来越聪明,越来越成熟,是我们老了吗?又跑题了,串口,自从我开始开发产品从来没离开过串口。因为我总要和计算机或其他的mcu说话,而串口是最简单和经济的方式。传统的也是最难舍弃,stm8的串口资源很丰厚,都有两个。好些年前,要用双串口除了使用专业芯片外只能选择华邦的芯片,说实话它那时真的很贵。Avr也有双串口的,所以我一见双串口的芯片,总是兴奋。大概得了串口恐惧症了。看代码:CLK_PCKENR1|=0X08;//开启时钟LINUART_BRR2=0X1;LINUART_BRR1=0X1A;//19200BPS

网站上整理的免费分享LINUART_CR2=0XAC;//8,n,1开启发送和接受中断上面是初始化部分,很是简单自己看看吧。我接下来要用串口中断做的事情很无聊,我要实现无论串口接收到一个什么数据,都要返回该数据并加发0x55,0xaa。实时上这个协议一点用处都没有,我希望大家开发产品的时候有串口协议时,如果资源够用,又不愿自己写时,使用modobus协议吧,真的很好用。下面是中断程序@far@interruptvoidUSART_TX(void){switch(status){case0:LINUART_DR=0X55;status=1;break;case1:LINUART_DR=0XAA;status=2;break;case2:LINUART_CR2=0X2C;status=0;break;}//数据空中断只能写dr清除,所以只能禁止它

网站上整理的免费分享return;}@far@interruptvoidUSART_RX(void){unsignedcharx;x=LINUART_DR;//读数据自动清除中断标志LINUART_DR=x;//同时清除发送空中断标志LINUART_CR2=0XAC;//所以可以打开发送空中断了status=0;return;}同样在向量表中改成这样{0x82,USART_TX},/*irq20*/{0x82,USART_RX},/*irq21*/在这个简单的基础上,就可以开发自己的协议了。我用串口只使用这么多功能,别的如lin,idra,或是别的都是以后的事了。和兄弟们说声再见,下次在写时就是正交编码和spi了。第五节:正交编码和疑惑今天去公司,找遍了废品堆都没有找到一只编码器,没办法只好从半成

网站上整理的免费分享品上拆下来一个,大家不要说是我做的,不然老板会很生气。正交计数方法很多,软件的,cpld的,芯片的都可以,但cpu上集成了我们为什么不用,我没理由不选带正交功能的stm8,因为他是8bit的,因为他价格据说很便宜,32bit的cpu大多是带这个功能的包括dsp,我总是说在我的产品里他是大马,我的产品是小车,其实是我不愿去啃32bit的大部头。写完这篇我下定决心要使用stm32了到时候兄弟们一定要帮助我,就当是扶贫吧。***的三合一板使用的芯片是s207s8t6,44脚的,time1的两个输入段为pc1,和pc2,我将编码器的a,b相分别接在PC1,PC2上。接上VCC和gnd,电路的工作算是完成,接下来都是软件的工作。在此之前看看stm32的正交编码接口应用笔记吧,上面对原理描述的很清楚,比我说的要有条理,我就不说了。看下面的代码//下面是初始化部分CLK_PCKENR1|=0X80;//开启time1时钟TIM1_SMCR|=0X3;TIM1_CCMR1|=0X1;//工作在编码器模式3//CC1MAPTI1FP1TI2FP2CH1CH2TIM1_CCMR2|=0X1;//CC2MAPTIM1_ARRH=0XEA;TIM1_ARRL=0X60;TIM1_IER|=0X1;//开中断//60000产生溢出TIM1_CNTR=cnt_start=30000;//我要有个大的初始化值//正好是满量程的一半TIM1_CR1=0X1;//启动计数通过上面简单的配置,time1正是工作了,旋动编码器,可以看到TIM1_CNTR的数据变动,我的1000线编码每转一圈产生4000个数。在我的中断和主程序里做了处理,可计数范围扩展到32bit,算是基本

网站上整理的免费分享达到了我的要求。有一件事要说一下,读TIM1_CNTR时要先读高位,再读低位。Stm8的工作告一段落,本来还要写spi的,可是还要搭外围电路,等一些天吧,我把ad7705接上,手中有十几片闲置的。今天无意中在中断里做了long型数据加法,编译时居然出错,翻了翻编译器手册,没找到原因,希望知道的朋友告诉我一声。没有使用意法的库,是因为我觉得使用它不利于入门,虽然它结构优美。做项目的时候再用吧。STM8的C语言编程(1)--基本程序与启动代码分析现在几乎所有的单片机都能用C语言编程了,采用C语言编程确实能带来很多好处,至少可读性比汇编语言强多了。在STM8的开发环境中,可以通过新建一个工程,自动地建立起一个C语言的框架,生成后开发环境会自动生成2个C语言的程序,一个是main.c,另一个是stm8_interrupt_vector.c。main.c中就是一个空的main()函数,如下所示:/***Copyright(c)2002-2005STMicroelectronics*/main(){while(1);}而在stm8_interrupt_vector.c中,就是声明了对应该芯片的中断向量,如下所示:/***/typedefvoid@far(*interrupt_handler_t)(void);BASICINTERRUPTVECTORTABLEFORSTM8devicesCopyright(c)2007STMicroelectronics

网站上整理的免费分享structinterrupt_vector{unsignedcharinterrupt_instruction;interrupt_handler_tinterrupt_handler;};@far@interruptvoidNonHandledInterrupt(void){/*inordertodetectunexpectedeventsduringdevelopment,itisrecommendedtosetabreakpointonthefollowinginstruction*/return;}externvoid_stext();/*startuproutine*/structinterrupt_vectorconst_vectab[]={{0x82,(interrupt_handler_t)_stext},/*reset*/{0x82,NonHandledInterrupt},/*trap*/{0x82,NonHandledInterrupt},/*irq0*/{0x82,NonHandledInterrupt},/*irq1*/{0x82,NonHandledInterrupt},/*irq2*/{0x82,NonHandledInterrupt},/*irq3*/{0x82,NonHandledInterrupt},/*irq4*/{0x82,NonHandledInterrupt},/*irq5*/{0x82,NonHandledInterrupt},/*irq6*/{0x82,NonHandledInterrupt},/*irq7*/{0x82,NonHandledInterrupt},/*irq8*/{0x82,NonHandledInterrupt},/*irq9*/{0x82,NonHandledInterrupt},/*irq10*/{0x82,NonHandledInterrupt},/*irq11*/{0x82,NonHandledInterrupt},/*irq12*/{0x82,NonHandledInterrupt},/*irq13*/{0x82,NonHandledInterrupt},/*irq14*/{0x82,NonHandledInterrupt},/*irq15*/{0x82,NonHandledInterrupt},/*irq16*/{0x82,NonHandledInterrupt},/*irq17*/{0x82,NonHandledInterrupt},/*irq18*/

网站上整理的免费分享{0x82,NonHandledInterrupt},/*irq19*/{0x82,NonHandledInterrupt},/*irq20*/{0x82,NonHandledInterrupt},/*irq21*/{0x82,NonHandledInterrupt},/*irq22*/{0x82,NonHandledInterrupt},/*irq23*/{0x82,NonHandledInterrupt},/*irq24*/{0x82,NonHandledInterrupt},/*irq25*/{0x82,NonHandledInterrupt},/*irq26*/{0x82,NonHandledInterrupt},/*irq27*/{0x82,NonHandledInterrupt},/*irq28*/{0x82,NonHandledInterrupt},/*irq29*/};在stm8_interrupt_vector.c中,除了定义了中断向量表外,还定义了空的中断服务程序,用于那些不用的中断。当然在自动建立时,所有的中断服务都是空的,因此,除了第1个复位的向量外,其它都指向那个空的中断服务函数。生成框架后,就可以用Build菜单下的RebuildAll对项目进行编译和连接,生成所需的目标文件,然后就可以加载到STM8的芯片中,这里由于main()函数是一个空函数,因此没有任何实际的功能。不过我们可以把这个框架对应的汇编代码反出来,看看C语言生成的代码,这样可以更深入地了解C语言编程的特点。生成的代码包括4个部分,如图1、图2、图3、图4所示。

网站上整理的免费分享图1图2图3图4图1显示的是从内存地址8000H开始的中断向量表,中断向量表中的第1行82008083H为复位后单片机运行的第1跳指令的地址。从表中可以看出,单片机复位后,将从8083H开始运行。其它行的中断向量都指向同一个位置的中断服务程序80D0H。图2显示的是3个字节,前2个字节8083H为复位后的第1条指令的地址,第3个字节是一个常量0,后面的启动代码要用到。图3显示的是启动代码,启动代码中除了初始化堆栈指针外,就是初始化RAM单元。由于目前是一个空的框架,因此在初始化完堆栈指针(设置成0FFFH)后,由于8082H单元

网站上整理的免费分享的内容为0,因此程序就跳到了80B1H,此处是一个循环,将RAM单元从0到5初始化成0。然后由于寄存器X设置成0100H,就直接通过CALLmain进入C的main()函数。图4显示的是main()函数和中断服务函数,main()函数对应的代码就是一个无限的循环,而中断服务函数就一条指令,即中断返回指令。通过分析,可以看出用C语言编程时,比汇编语言编程时,就是多出了一段启动代码。STM8的C语言编程(2)--变量空间的分配采用C这样的高级语言,其实可以不用关心变量在存储器空间中是如何具体分配的。但如果了解如何分配,对编程还是有好处的,尤其是在调试时。例如下面的程序定义了全局变量数组buffer和一个局部变量i,在RAM中如何分配的呢?/***Copyright(c)2002-2005STMicroelectronics*/unsignedcharbuffer[10];main(){unsignedchari;for(i=0;i<10;i++){buffer[i]=0x55;}}我们可以通过DEBUG中的反汇编窗口,看到如下的对应代码://定义局部变量//定义全局变量

网站上整理的免费分享从这段代码中可以看到,全局变量buffer被分配到空间从地址0000H到0009H。而局部变量i则在堆栈空间中分配,通过PUSHA指令,将堆栈指针减1,腾出一个字节的空间,而SP+1指向的空间就是分配给局部变量使用的空间。由此可以得出初步的结论,对于全局变量,内存分配是从低地址0000H开始向上分配的。而局部变量则是在堆栈空间中分配。另外从上一篇文章中,可以知道堆栈指针初始化时为0FFFH。而根据PUSH指令的定义,当压栈后堆栈指针减1。因此堆栈是从上往下使用的。因此根据内存分配和堆栈使用规则,我们在程序设计时,不能定义过多的变量,免得没有空间给堆栈使用。换句话说,当定义变量时,一定要考虑到堆栈空间,尤其是那些复杂的系统,程序调用层数多,这样就会占用大量的堆栈空间。总之,在单片机的程序设计时,由于RAM空间非常有限,要充分考虑到全局变量、局部变量、程序调用层数和中断服务调用对空间的占用。STM8的C语言编程(3)――GPIO输出与前些日子写的用汇编语言进行的实验一样,从今天开始,要在ST的三合一开发板上,用C语言编写程序,进行一系列的实验。首先当然从最简单的LED指示灯闪烁的实验开始。开发板上的LED1接在STM8的PD3上,因此要将PD3设置成输出模式,为了提高高电平时的输出电流,要将其设置成推挽输出方式。这主要通过设置对应的DDR/CR1/CR2寄存器实现。利用ST的开发工具,先生成一个C语言程序的框架,然后修改其中的main.c,修改后的代码如下。编译通过后,下载到开发板,运行程序,可以看到LED1在闪烁,且闪烁的频率为5HZ。/**

网站上整理的免费分享*Copyright(c)2002-2005STMicroelectronics*/#include"STM8S207C_S.h"//函数功能:延时函数//输入参数:ms--要延时的毫秒数,这里假设CPU的主频为2MHZ//输出参数:无//返回值:无//备{unsignedchari;while(ms!=0){for(i=0;i<250;i++){}for(i=0;i<75;i++){}ms--;}}//函数功能:主函数//初始化GPIO端口PD3,驱动PD3为高电平和低电平//输入参数:ms--要延时的毫秒数,这里假设CPU的主频为2MHZ//输出参数:无//返回值:无//备main(){PD_DDR=0x08;PD_CR1=0x08;PD_CR2=0x00;while(1){//将PD3设置成推挽输出注:无注:无voidDelayMS(unsignedintms)

网站上整理的免费分享PD_ODR=PD_ODR|0x08;DelayMS(100);DelayMS(100);}}PD_ODR=PD_ODR&0xF7;//将PD3的输出设置成1//将PD3的输出设置成0//延时100MS//延时100MS需要注意的是,当生成完框架后,为了能方便使用STM8的寄存器名字,必须包括STM8S207C_S.h,最好将该文件拷贝到C:ProgramFilesSTMicroelectronicsst_toolsetinclude目录下,拷贝到工程目录下。或者将该路径填写到该工程的Settings…中的CCompiler选项Preprocessor的Additionalinclude中,这样编译时才会找到该文件。STM8的C语言编程(4)――GPIO输出和输入今天要进行的实验,是利用GPIO进行输入和输出。在ST的三合一开发板上,按键接在GPIO的PD7上,LED接在GPIO的PD3上,因此我们要将GPIO的PD7初始化成输入,PD3初始化成输出。关于GPIO的引脚设置,主要是要初始化方向寄存器DDR,控制寄存器1(CR1)和控制寄存器2(CR2),寄存器的每一位对应GPIO的每一个引脚。具体的设置功能定义如下:DDR0000111CR1001101X0101001CR2引脚设置悬浮输入上拉输入中断悬浮输入中断上拉输入开漏输出推挽输出输出(最快速度为10MHZ)另外,输出引脚对应的寄存器为ODR,输入引脚对应的寄存器为IDR。下面的程序是检测按键的状态,当按键按下时,点亮LED,当按键抬起时,熄灭LED。同样也是利用ST的开发工具,先生成一个C语言程序的框架,然后修改其中的main.c,修改后的代码如下。

网站上整理的免费分享编译通过后,下载到开发板,运行程序,按下按键,LED就点亮,抬起按键,LED就熄灭了。另外,要注意,将STM8S207C_S.h拷贝到当前项目的目录下。//程序描述:检测开发板上的按键,若按下,则点亮LED,若抬起,则熄灭LED////按键接在MCU的GPIO的PD7上LED接在MCU的GPIO的PD3上#include"STM8S207C_S.h"main(){PD_DDR=0x08;PD_CR1=0x08;PD_CR2=0x00;while(1){if((PD_IDR&0x80)==0x80){PD_ODR=PD_ODR&0xF7;LED}else{PD_ODR=PD_ODR|0x08;}}}//否则,将PD3的输出设置成1,点亮LED//如果PD7为1,则将PD3的输出设置成0,熄灭//读入PD7的引脚信号//进入无限循环//将PD3设置成推挽输出STM8的C语言编程(5)--8位定时器应用之一

网站上整理的免费分享在STM8单片机中,有多种定时器资源,既有8位的定时器,也有普通的16位定时器,还有高级的定时器。今天的实验是用最简单的8位定时器TIM4来进行延时,然后驱动LED闪烁。为了简单起见,这里是通过程序查询定时器是否产生更新事件,来判断定时器的延时是否结束。同样还是利用ST的开发工具,生成一个C程序的框架,然后修改其中的main.c,修改后的代码如下。编译通过后,下载到开发板,运行程序,可以看到LED在闪烁,或者用示波器可以在LED引脚上看到方波。在这里要特别提醒的是,从ST给的手册上看,这个定时器中的计数器是一个加1计数器,但本人在实验过程中感觉不太对,经过反复的实验,我认为应该是一个减1计数器(也许是我拿的手册不对,或许是理解上有误)。例如,当给定时器中的自动装载寄存器装入255时,产生的方波频率最小,就象下面代码中计算的那样,产生的方波频率为30HZ左右。若初始化时给自动装载寄存器装入1,则产生的方波频率最大,大约为3.9K左右。也就是说实际的分频数为ARR寄存器的值+1。//程序描述:通过初始化定时器4,进行延时,驱动LED闪烁//LED接在MCU的GPIO的PD3上#include"STM8S207C_S.h"main(){//首先初始化GPIOPD_DDR=0x08;PD_CR1=0x08;PD_CR2=0x00;//然后初始化定时器4TIM4_IER=0x00;TIM4_EGR=0x01;TIM4_PSCR=0x07;TIM4_ARR=255;TIM4_CNTR=255;TIM4_CR1=0x01;//禁止中断//允许产生更新事件//计数器时钟=主时钟/128=2MHZ/128//相当于计数器周期为64uS//设定重装载时的寄存器值,255是最大值//设定计数器的初值//b0=1,允许计数器工作//将PD3设置成推挽输出//定时周期=(ARR+1)*64=16320uS//b1=0,允许更新//设置控制器,启动定时器

网站上整理的免费分享while(1){//进入无限循环//等待更新标志//LED驱动信号取反while((TIM4_SR1&0x81)==0x00);TIM4_SR1=0x00;PD_ODR=PD_ODR^0x08;}}//清除更新标志//LED闪烁频率=2MHZ/128/255/2=30.63STM8的C语言编程(6)--8位定时器应用之二今天进行的实验依然是用定时器4,只不过改成了用中断方式来实现,由定时器4的中断服务程序来驱动LED的闪烁。实现中断方式的关键点有几个,第一个关键点就是要打开定时器4的中断允许位,在定时器4的IER寄存器中有定义。第二个关键点,就是打开CPU的全局中断允许位,在汇编语言中,就是执行RIM指令,在C语言中,用下列语句实现:_asm("rim");第3个关键点就是中断服务程序的框架或写法,中断服务程序的写法如下:@far@interruptvoidTIM4_UPD_OVF_IRQHandler(void){//下面是中断服务程序的实体}第4个关键点就是要设置中断向量,即将中断服务程序的入口填写到中断向量表中,如下所示,将IRQ23对应的中断服务程序的入口填写成TIM4_UPD_OVF_IRQHandlerstructinterrupt_vectorconst_vectab[]={{0x82,(interrupt_handler_t)_stext},/*reset*/{0x82,NonHandledInterrupt},/*trap*/{0x82,NonHandledInterrupt},/*irq0*/{0x82,NonHandledInterrupt},/*irq1*/{0x82,NonHandledInterrupt},/*irq2*/{0x82,NonHandledInterrupt},/*irq3*/{0x82,NonHandledInterrupt},/*irq4*/{0x82,NonHandledInterrupt},/*irq5*/{0x82,NonHandledInterrupt},/*irq6*/

网站上整理的免费分享{0x82,NonHandledInterrupt},/*irq7*/{0x82,NonHandledInterrupt},/*irq8*/{0x82,NonHandledInterrupt},/*irq9*/{0x82,NonHandledInterrupt},/*irq10*/{0x82,NonHandledInterrupt},/*irq11*/{0x82,NonHandledInterrupt},/*irq12*/{0x82,NonHandledInterrupt},/*irq13*/{0x82,NonHandledInterrupt},/*irq14*/{0x82,NonHandledInterrupt},/*irq15*/{0x82,NonHandledInterrupt},/*irq16*/{0x82,NonHandledInterrupt},/*irq17*/{0x82,NonHandledInterrupt},/*irq18*/{0x82,NonHandledInterrupt},/*irq19*/{0x82,NonHandledInterrupt},/*irq20*/{0x82,NonHandledInterrupt},/*irq21*/{0x82,NonHandledInterrupt},/*irq22*/{0x82,TIM4_UPD_OVF_IRQHandler},/*irq23*/{0x82,NonHandledInterrupt},/*irq24*/{0x82,NonHandledInterrupt},/*irq25*/{0x82,NonHandledInterrupt},/*irq26*/{0x82,NonHandledInterrupt},/*irq27*/{0x82,NonHandledInterrupt},/*irq28*/{0x82,NonHandledInterrupt},/*irq29*/};解决了以上4个关键点,我们就能很轻松地用C语言实现中断服务了。同样还是利用ST的开发工具,生成一个C程序的框架,然后修改其中的main.c,修改后的代码如下。另外还要修改stm8_interrupt_vector.c。编译通过后,下载到开发板,运行程序,可以看到LED在闪烁,或者用示波器可以在LED引脚上看到方波。修改后的main.c如下://程序描述:通过初始化定时器4,以中断方式驱动LED闪烁//LED接在MCU的GPIO的PD3上#include"STM8S207C_S.h"main(){

网站上整理的免费分享//首先初始化GPIOPD_DDR=0x08;PD_CR1=0x08;PD_CR2=0x00;//然后初始化定时器4TIM4_IER=0x00;TIM4_EGR=0x01;TIM4_PSCR=0x07;TIM4_ARR=255;TIM4_CNTR=255;TIM4_CR1=0x01;//禁止中断//允许产生更新事件//计数器时钟=主时钟/128=2MHZ/128//相当于计数器周期为64uS//设定重装载时的寄存器值,255是最大值//设定计数器的初值//b0=1,允许计数器工作//将PD3设置成推挽输出//定时周期=(ARR+1)*64=16320uS//b1=0,允许更新//设置控制器,启动定时器TIM4_IER=0x01;_asm("rim");while(1){}}//函数功能:定时器4的更新中断服务程序//输入参数:无//输出参数:无//返回值:无@far@interruptvoidTIM4_UPD_OVF_IRQHandler(void){TIM4_SR1=0x00;//清除更新标志//LED驱动信号取反PD_ODR=PD_ODR^0x08;}修改后的stm8_interrupt_vector.c如下:/***/BASICINTERRUPTVECTORTABLEFORSTM8devicesCopyright(c)2007STMicroelectronics//允许更新中断//允许CPU全局中断//进入无限循环//LED闪烁频率=2MHZ/128/255/2=30.63

网站上整理的免费分享typedefvoid@far(*interrupt_handler_t)(void);structinterrupt_vector{unsignedcharinterrupt_instruction;interrupt_handler_tinterrupt_handler;};@far@interruptvoidNonHandledInterrupt(void){/*inordertodetectunexpectedeventsduringdevelopment,itisrecommendedtosetabreakpointonthefollowinginstruction*/return;}externvoid_stext();/*startuproutine*/extern@far@interruptvoidTIM4_UPD_OVF_IRQHandler(void);structinterrupt_vectorconst_vectab[]={{0x82,(interrupt_handler_t)_stext},/*reset*/{0x82,NonHandledInterrupt},/*trap*/{0x82,NonHandledInterrupt},/*irq0*/{0x82,NonHandledInterrupt},/*irq1*/{0x82,NonHandledInterrupt},/*irq2*/{0x82,NonHandledInterrupt},/*irq3*/{0x82,NonHandledInterrupt},/*irq4*/{0x82,NonHandledInterrupt},/*irq5*/{0x82,NonHandledInterrupt},/*irq6*/{0x82,NonHandledInterrupt},/*irq7*/{0x82,NonHandledInterrupt},/*irq8*/{0x82,NonHandledInterrupt},/*irq9*/{0x82,NonHandledInterrupt},/*irq10*/{0x82,NonHandledInterrupt},/*irq11*/{0x82,NonHandledInterrupt},/*irq12*/{0x82,NonHandledInterrupt},/*irq13*/{0x82,NonHandledInterrupt},/*irq14*/{0x82,NonHandledInterrupt},/*irq15*/

网站上整理的免费分享{0x82,NonHandledInterrupt},/*irq16*/{0x82,NonHandledInterrupt},/*irq17*/{0x82,NonHandledInterrupt},/*irq18*/{0x82,NonHandledInterrupt},/*irq19*/{0x82,NonHandledInterrupt},/*irq20*/{0x82,NonHandledInterrupt},/*irq21*/{0x82,NonHandledInterrupt},/*irq22*/{0x82,TIM4_UPD_OVF_IRQHandler},/*irq23*/{0x82,NonHandledInterrupt},/*irq24*/{0x82,NonHandledInterrupt},/*irq25*/{0x82,NonHandledInterrupt},/*irq26*/{0x82,NonHandledInterrupt},/*irq27*/{0x82,NonHandledInterrupt},/*irq28*/{0x82,NonHandledInterrupt},/*irq29*/};STM8的C语言编程(7)--16位定时器的中断应用在STM8中,除了有8位的定时器外,还有16位的定时器。今天进行的实验就是针对16位定时器2来进行的。除了计数单元为16位的,其它设置与前面8位的定时器基本一样。下面的程序也是采样中断方式,由定时器2的中断服务程序来驱动LED的闪烁。具体的程序代码如下,其它注意点见上一篇,另外要注意别忘了修改相应的中断向量。//程序描述:通过初始化定时器2,以中断方式驱动LED闪烁//LED接在MCU的GPIO的PD3上#include"STM8S207C_S.h"main(){//首先初始化GPIOPD_DDR=0x08;PD_CR1=0x08;PD_CR2=0x00;//然后初始化定时器4TIM2_IER=0x00;//禁止中断//将PD3设置成推挽输出

网站上整理的免费分享TIM2_EGR=0x01;TIM2_PSCR=0x01;//允许产生更新事件//计数器时钟=主时钟/128=2MHZ/2//相当于计数器周期为1uS//设定重装载时的寄存器值//注意必须保证先写入高8位,再写入低8位TIM2_ARRH=0xEA;TIM2_ARRL=0x60;TIM2_CNTRH=0xEA;TIM2_CNTRL=0x60;//定时周期=1*60000=60000uS=60msTIM2_CR1=0x01;//b0=1,允许计数器工作//设定计数器的初值//设定重装载时的寄存器的高8位//b1=0,允许更新//设置控制器,启动定时器TIM2_IER=0x01;_asm("rim");while(1){}}//函数功能:定时器4的更新中断服务程序//输入参数:无//输出参数:无//返回值:无@far@interruptvoidTIM2_UPD_IRQHandler(void){TIM2_SR1=0x00;//清除更新标志//LED驱动信号取反PD_ODR=PD_ODR^0x08;}//允许更新中断//允许CPU全局中断//进入无限循环//LED闪烁频率=2MHZ/2/60000/2=8.3

网站上整理的免费分享STM8的C语言编程(8)--UART应用串口通讯也是单片机应用中经常要用到,今天的实验就是利用STM8的UART资源,来进行串口通讯的实验。实验程序的功能是以中断方式接收串口数据,然后将接收到的数据以查询方式发送到串口。程序代码如下,首先要对STM8的UART进行初始化,初始化时要注意的是波特率寄存器的设置,当求出一个波特率的分频系数(一个16位的数)后,要将高4位和低4位写到BRR2中,而将中间的8位写到BRR1中,并且必须是先写BRR2,再写BRR1。同样也是利用ST的开发工具,生成一个C语言的框架,然后修改其中的main.c,同时由于需要用到中断服务,因此还要修改stm8_interrupt_vector.c。修改后,编译连接,然后下载到开发板上,再做一根与PC机相连的线,把开发板的串口与PC机的串口连接起来,注意,2、3脚要交叉。在PC机上运行超级终端,设置波特率为9600,然后每按下一个按键,屏幕上就显示对应的字符。修改后的main.c和stm8_interrupt_vector.c如下://程序描述:初始化UART,以中断方式接收字符,以查询方式发送//UART通讯参数:9600bps,8位数据,1位停止位,无校验#include"STM8S207C_S.h"//函数功能:初始化UART//输入参数:无//输出参数:无//返回值:无//备{LINUART_CR2=0;LINUART_CR1=0;//禁止UART发送和接收//b5=0,允许UART注:无voidUART3_Init(void)//b2=0,禁止校验LINUART_CR3=0;//b5,b4=00,1个停止位

网站上整理的免费分享//设置波特率,必须注意以下几点://////(1)必须先写BRR2(2)BRR1存放的是分频系数的第11位到第4位,(3)BRR2存放的是分频系数的第15位到第12位,和第3位到第0位//例如对于波特率位9600时,分频系数=2000000/9600=208//对应的十六进制数为00D0,BBR1=0D,BBR2=00LINUART_BRR2=0;LINUART_BRR1=0x0d;//实际的波特率分频系数为00D0(208)//对应的波特率为2000000/208=9600LINUART_CR2=0x2C;//b3=1,允许发送//b2=1,允许接收//b5=1,允许产生接收中断}//函数功能:从UART3发送一个字符//输入参数:ch--要发送的字符//输出参数:无//返回值:无//备{while((LINUART_SR&0x80)==0x00);//若发送寄存器不空,则等待LINUART_DR=ch;}main(){//首先初始化UART3UART3_Init();_asm("rim");while(1){}}//允许CPU全局中断//进入无限循环//将要发送的字符送到数据寄存器注:无voidUART3_SendChar(unsignedcharch)

网站上整理的免费分享//函数功能:UART3的接收中断服务程序//输入参数:无//输出参数:无//返回值:无@far@interruptvoidUART3_Recv_IRQHandler(void){unsignedcharch;ch=LINUART_DR;UART3_SendChar(ch);}//读入接收到的字符//将字符发送出去/***/BASICINTERRUPTVECTORTABLEFORSTM8devicesCopyright(c)2007STMicroelectronicstypedefvoid@far(*interrupt_handler_t)(void);structinterrupt_vector{unsignedcharinterrupt_instruction;interrupt_handler_tinterrupt_handler;};@far@interruptvoidNonHandledInterrupt(void){/*inordertodetectunexpectedeventsduringdevelopment,itisrecommendedtosetabreakpointonthefollowinginstruction*/return;}externvoid_stext();/*startuproutine*/extern@far@interruptvoidUART3_Recv_IRQHandler();structinterrupt_vectorconst_vectab[]={{0x82,(interrupt_handler_t)_stext},/*reset*/

网站上整理的免费分享{0x82,NonHandledInterrupt},/*trap*/{0x82,NonHandledInterrupt},/*irq0*/{0x82,NonHandledInterrupt},/*irq1*/{0x82,NonHandledInterrupt},/*irq2*/{0x82,NonHandledInterrupt},/*irq3*/{0x82,NonHandledInterrupt},/*irq4*/{0x82,NonHandledInterrupt},/*irq5*/{0x82,NonHandledInterrupt},/*irq6*/{0x82,NonHandledInterrupt},/*irq7*/{0x82,NonHandledInterrupt},/*irq8*/{0x82,NonHandledInterrupt},/*irq9*/{0x82,NonHandledInterrupt},/*irq10*/{0x82,NonHandledInterrupt},/*irq11*/{0x82,NonHandledInterrupt},/*irq12*/{0x82,NonHandledInterrupt},/*irq13*/{0x82,NonHandledInterrupt},/*irq14*/{0x82,NonHandledInterrupt},/*irq15*/{0x82,NonHandledInterrupt},/*irq16*/{0x82,NonHandledInterrupt},/*irq17*/{0x82,NonHandledInterrupt},/*irq18*/{0x82,NonHandledInterrupt},/*irq19*/{0x82,NonHandledInterrupt},/*irq20*/{0x82,UART3_Recv_IRQHandler},/*irq21*/{0x82,NonHandledInterrupt},/*irq22*/{0x82,NonHandledInterrupt},/*irq23*/{0x82,NonHandledInterrupt},/*irq24*/{0x82,NonHandledInterrupt},/*irq25*/{0x82,NonHandledInterrupt},/*irq26*/{0x82,NonHandledInterrupt},/*irq27*/{0x82,NonHandledInterrupt},/*irq28*/{0x82,NonHandledInterrupt},/*irq29*/};STM8与汇编语言(9)--EEPROM应用

网站上整理的免费分享EEPROM是单片机应用系统中经常会用到的存储器,它主要用来保存一些掉电后需要保持不变的数据。在以前的单片机系统中,通常都是在单片机外面再扩充一个EEPROM芯片,这种方法除了增加成本外,也降低了可靠性。现在,很多单片机的公司都推出了集成有小容量EEPROM的单片机,这样就方便了使用,降低了成本,提高了可靠性。STM8单片机芯片内部也集成有EEPROM,容量从640字节到2K字节。最为特色的是,在STM8单片机中,对EEPROM的访问就象常规的RAM一样,非常方便。EEPROM的地址空间与内存是统一编址的,地址从004000H开始,大小根据不同的芯片型号而定。下面的实验程序,就是先给EEPROM中的第一个单元004000H写入55H,然后再读到全局变量ch中。同样还是利用ST的开发工具,生成一个C语言程序的框架,然后修改其中的main.c,修改后的代码如下。//程序描述:对芯片内部的EEPROM存储单元进行实验#include"STM8S207C_S.h"unsignedcharch;main(){unsignedchar*p;p=(unsignedchar*)0x4000;//对数据EEPROM进行解锁do{FLASH_DUKR=0xae;FLASH_DUKR=0x56;//写入第一个密钥//写入第二个密钥//若解锁未成功,则重新再来//指针p指向芯片内部的EEPROM第一个单元}while((FLASH_IAPSR&0x08)==0);*p=0xaa;//写入第一个字节//等待写操作成功while((FLASH_IAPSR&0x04)==0);ch=*p;while(1){//将写入的内容读到变量ch中

网站上整理的免费分享;}}这里要注意的是,2个密钥的顺序,与STM8的用户手册上是相反的,如果按照手册上的顺序,就会停留在do…while循环中。具体原因,也不是很清楚,也可能是我拿到的手册(中文和英文的都一样)太旧了,或者是理解有误。另外,上面的实验程序中,ch不能为局部变量,否则的话,在调试环境中跟踪ch变量时,显示的结果就不对,通过反汇编,我觉得是编译有问题,当定义成局部变量时,ch=*p的汇编代码如下:main.c:23ch=*p;0x7B010x970x1E020xF60x97LDLDLDLDLDWXL,AX,(0x02,SP)LDLDA,(X)XL,A//将写入的内容读到变量ch中A,(0x01,SP)LDLDA,(0x01,SP)X,(0x02,SP)XL,ALDWA,(X)XL,A0x80f00x80f20x80f30x80f50x80f6如果将ch定义成全局变量,则汇编代码为:main.c:22ch=*p;0x1E010xF60xB700LDWLDLDA,(X)0x00,A//将写入的内容读到变量ch中X,(0x01,SP)LDLDLDWA,(X)0x00,AX,(0x01,SP)0x80ef0x80f10x80f2这一段代码的分析仅供参考,本人使用的开发环境为STVD4.1.0,编译器版本号为:COSMIC的CxSTM84.2.4。STM8的C语言编程(10)--修改CPU的时钟在有些单片机的应用系统中,并不需要CPU运行在多高的频率。在低频率下运行,芯片的功耗会大大下降。STM8单片机在运行过程中,可以随时修改CPU运行时钟频率,非常方便。实现这一功能,主要涉及到时钟分频寄存器(CLK_CKDIVR)。时钟分频寄存器是一个8位的寄存器,高3位保留,位4和位3用于定义高速内部时钟的预分频,而位2到位0则用于CPU时钟的分频。这5位的详细定义如下:位4001位3010124高速内部时钟的分频系数

网站上整理的免费分享1位2位1位8163264128CPU时钟的分频系数00110011假设我们使用内部的高速RC振荡器,其频率为16MHZ,当位4为0,位3为1时,则内部高速时钟的分频系数为2,因此输出的主时钟为8MHZ。当位2为0,位1为1,位0为0时,CPU时钟的分频系数为4,即CPU时钟=主时钟/4=2MHZ。下面的实验程序首先将CPU的运行时钟设置在8MHZ,然后快速闪烁LED指示灯。接着,通过修改主时钟的分频系数和CPU时钟的分频系数,将CPU时钟频率设置在500KHZ,然后再慢速闪烁LED指示灯。通过观察LED指示灯的闪烁频率,可以看到,同样的循环代码,由于CPU时钟频率的改变,闪烁频率和时间长短都发生了变化。同样还是利用ST的开发工具,生成一个C语言程序的框架,然后修改其中的main.c,修改后的代码如下。修改后的代码编译连接后,就可以下载到开发板上,运行后会看到LED的闪烁频率有明显的变化。//程序描述:通过修改CPU时钟的分频系数,来改变CPU的运行速度#include"STM8S207C_S.h"//函数功能:延时函数//输入参数:ms--要延时的毫秒数,这里假设CPU的主频为2MHZ//输出参数:无//返回值:无//备{unsignedchari;while(ms!=0){for(i=0;i<250;i++)注:无voidDelayMS(unsignedintms)

网站上整理的免费分享{}for(i=0;i<75;i++){}ms--;}}main(){inti;PD_DDR=0x08;PD_CR1=0x08;PD_CR2=0x00;CLK_SWR=0xE1;for(;;){//下面设置CPU时钟分频器,使得CPU时钟=主时钟//通过发光二极管,可以看出,程序运行的速度确实明显提高了CLK_CKDIVR=0x08;for(i=0;i<10;i++){PD_ODR=0x08;DelayMS(100);PD_ODR=0x00;DelayMS(100);}//下面设置CPU时钟分频器,使得CPU时钟=主时钟/4//通过发光二极管,可以看出,程序运行的速度确实明显下降了CLK_CKDIVR=0x1A;for(i=0;i<10;i++)//主时钟=16MHZ/8//CPU时钟=主时钟/4=500KHZ//主时钟=16MHZ/2//CPU时钟=主时钟=8MHZ//选择芯片内部的16MHZ的RC振荡器为主时钟//将PD3设置成推挽输出//进入无限循环

网站上整理的免费分享{PD_ODR=0x08;DelayMS(100);PD_ODR=0x00;DelayMS(100);}}}STM8的C语言编程(11)--切换时钟源STM8单片机的时钟源非常丰富,芯片内部既有16MHZ的高速RC振荡器,也有128KHZ的低速RC振荡器,外部还可以接一个高速的晶体振荡器。在系统运行过程中,可以根据需要,自由地切换。单片机复位后,首先采用的是内部的高速RC振荡器,且分频系数为8,因此CPU的上电运行的时钟频率为2MHZ。切换时钟源,主要涉及到的寄存器有:主时钟切换寄存器CLK_SWR和切换控制寄存器CLK_SWCR。主时钟切换寄存器的复位值为0xe1,表示切换到内部的高速RC振荡器上。当往该寄存器写入0xb4时,表示切换到外部的高速晶体振荡器上。在实际切换过程中,应该先将切换控制寄存器中的SWEN(第1位)设置成1,然后设置CLK_SWCR的值,最后要判断切换控制寄存器中的SWIF标志是否切换成功。下面的实验程序首先将主时钟源切换到外部的晶体振荡器上,振荡频率为8MHZ,然后,然后快速闪烁LED指示灯。接着,将主时钟源又切换到内部的振荡器上,振荡频率为2MHZ,然后再慢速闪烁LED指示灯。通过观察LED指示灯的闪烁频率,可以看到,同样的循环代码,由于主时钟源的改变的改变,闪烁频率和时间长短都发生了变化。同样还是利用ST的开发工具,生成一个C语言程序的框架,然后修改其中的main.c,修改后的代码如下。//程序描述:通过切换CPU的主时钟源,来改变CPU的运行速度#include"STM8S207C_S.h"//函数功能:延时函数//输入参数:ms--要延时的毫秒数,这里假设CPU的主频为2MHZ//输出参数:无//返回值:无

网站上整理的免费分享//备{注:无voidDelayMS(unsignedintms)unsignedchari;while(ms!=0){for(i=0;i<250;i++){}for(i=0;i<75;i++){}ms--;}}main(){inti;//将PD3设置成推挽输出,以便推动LEDPD_DDR=0x08;PD_CR1=0x08;PD_CR2=0x00;//启动外部高速晶体振荡器CLK_ECKR=0x01;//允许外部高速振荡器工作while((CLK_ECKR&0x02)==0x00);//等待外部高速振荡器准备好//注意,复位后CPU的时钟源来自内部的RC振荡器for(;;){//下面将CPU的时钟源切换到外部的高速晶体振荡器上,在开发板上的频率为8MHZ//通过发光二极管,可以看出,程序运行的速度确实明显提高了CLK_SWCR=CLK_SWCR|0x02;CLK_SWR=0xB4;//SWEN<-1//选择芯片外部的高速振荡器为主时钟//进入无限循环

网站上整理的免费分享while((CLK_SWCR&0x08)==0);//等待切换成功CLK_SWCR=CLK_SWCR&0xFD;for(i=0;i<10;i++){PD_ODR=0x08;DelayMS(100);PD_ODR=0x00;DelayMS(100);}//下面将CPU的时钟源切换到内部的RC振荡器上,由于CLK_CKDIVR的复位值为0x18//所以16MHZ的RC振荡器要经过8分频后才作为主时钟,因此频率为2MHZ//通过发光二极管,可以看出,程序运行的速度确实明显下降了CLK_SWCR=CLK_SWCR|0x02;CLK_SWR=0xE1;//SWEN<-1//选择HSI为主时钟源//清除切换标志//LED高速闪烁10次while((CLK_SWCR&0x08)==0);//等待切换成功CLK_SWCR=CLK_SWCR&0xFD;for(i=0;i<10;i++){PD_ODR=0x08;DelayMS(100);PD_ODR=0x00;DelayMS(100);}}}//清除切换标志//LED低速闪烁10次STM8的C语言编程(12)--AD转换

网站上整理的免费分享在许多的单片机应用系统中,都需要A/D转换器,将模拟量转换成数字量。在STM8单片机中,提供的是10位的A/D,通道数随芯片不同而不同,少的有4个通道,多的则有16个通道。下面的实验程序首先对A/D输入进行采样,然后将采样结果的高8位(丢弃最低的2位),作为延时参数去调用延时子程序,然后再去驱动LED控制信号。因此不同的采样值,决定了LED的闪烁频率。当旋转ST三合一开发板上的电位器时,可以看到LED的闪烁频率发生变化。同样还是利用ST的开发工具,生成一个C语言程序的框架,然后修改其中的main.c,修改后的代码如下。//程序描述:通过AD模块,采样电位器的电压,改变LED的闪烁频率#include"STM8S207C_S.h"//函数功能:延时函数//输入参数:ms--要延时的毫秒数,这里假设CPU的主频为2MHZ//输出参数:无//返回值:无//备{unsignedchari;while(ms!=0){for(i=0;i<250;i++){}for(i=0;i<75;i++){}ms--;}}main(){inti;注:无voidDelayMS(unsignedintms)

网站上整理的免费分享//将PD3设置成推挽输出,以便推动LEDPD_DDR=0x08;PD_CR1=0x08;PD_CR2=0x00;//初始化A/D模块ADC_CR2=0x00;ADC_CR1=0x00;//A/D结果数据左对齐//ADC时钟=主时钟/2=1MHZ//ADC转换模式=单次//禁止ADC转换ADC_CSR=0x03;ADC_TDRL=0x20;for(;;){ADC_CR1=0x01;for(i=0;i<100;i++);//CR1寄存器的最低位置1,使能ADC转换//延时一段时间,至少7uS,保证ADC模块的上电完成//进入无限循环//选择通道3ADC_CR1=ADC_CR1|0x01;//再次将CR1寄存器的最低位置1//使能ADC转换while((ADC_CSR&0x80)==0);//等待ADC结束i=ADC_DRH;DelayMS(i);//读出ADC结果的高8位//延时一段时间//将PD3反相PD_ODR=PD_ODR^0x08;}}STM8的C语言编程(13)--蜂鸣器蜂鸣器是现在单片机应用系统中很常见的,常用于实现报警功能。为此STM8特别集成了蜂鸣器模块,应用起来非常方便。

网站上整理的免费分享在应用蜂鸣器模块时,首先要打开片内的低速RC振荡器(当然也能使用外部的高速时钟),其频率为128KHZ。然后通过设置蜂鸣器控制寄存器BEEP_CSR中的BEEPDIV[4:0]来获取8KHZ的时钟,再通过BEEPSEL最终产生1KHZ或2KHZ或4KHZ的蜂鸣器时钟,最后使能该寄存器中的BEEPEN位,产生蜂鸣器的输出。下面的实验程序首先初始化低速振荡器,然后启动蜂鸣器,再延时2.5秒,然后关闭蜂鸣器。同样还是利用ST的开发工具,生成一个汇编程序的框架,然后修改其中的main.c,修改后的代码如下。//程序描述:启动单片机中的蜂鸣器模块#include"STM8S207C_S.h"//函数功能:延时函数//输入参数:ms--要延时的毫秒数,这里假设CPU的主频为2MHZ//输出参数:无//返回值:无//备{unsignedchari;while(ms!=0){for(i=0;i<250;i++){}for(i=0;i<75;i++){}ms--;}}main(){inti;CLK_ICKR=CLK_ICKR|0x08;//打开芯片内部的低速振荡器LSI注:无voidDelayMS(unsignedintms)while((CLK_ICKR&0x10)==0);//等待振荡器稳定

网站上整理的免费分享//通过设置蜂鸣器控制寄存器,来打开蜂鸣器的功能//蜂鸣器控制寄存器的设置://BEEPDIV[1:0]=00//BEEPDIV[4:0]=0e//BEEPEN=1//蜂鸣器的输出频率=Fls/(8*(BEEPDIV+2))=128K/(8*16)=1KBEEP_CSR=0x2e;for(i=0;i<10;i++){DelayMS(250);}BEEP_CSR=0x1e;while(1);}//关闭蜂鸣器STM8的C语言编程(14)--PWM在单片机应用系统中,也常常会用到PWM信号输出,例如电机转速的控制。现在很多高档的单片机也都集成了PWM功能模块,方便用户的应用。对于PWM信号,主要涉及到两个概念,一个就是PWM信号的周期或频率,另一个就是PWM信号的占空比。例如一个频率为1KHZ,占空比为30%,有效信号为1的PWM信号,在用示波器测量时,就是高电平的时间为300uS,低电平的时间为700uS的周期波形。在单片机中实现PWM信号的功能模块,实际上就是带比较器的计数器模块。首先该计数器循环计数,例如从0到N,那么这个N就决定了PWM的周期,PWM周期=(N+1)*计数器时钟的周期。在计数器模块中一定还有一个比较器,比较器有2个输入,一个就是计数器的当前值,另一个是可以设置的数,这个数来自一个比较寄存器。当计数器的值小于比较寄存器的值时,输出为1(可以设置为0),当计数器的值大于或等于比较寄存器的值时,输出为0(也可设置为1,与前面对应)。了解了这个基本原理后,我们就可以使用STM8单片机中的PWM模块了。下面的实验程序首先将定时器2的通道2设置成PWM输出方式,然后通过设置自动装载寄存器TIM2_CCR2,决定PWM信号的周期。在程序的主循环中,循环修改占空比,先是从0逐渐递增到128,然后再从128递减到0。

网站上整理的免费分享当把下面的程序在ST的三合一板上运行时,可以看到发光二极管LD1逐渐变亮,然后又逐渐变暗,就这样循环往复。如果用示波器看,可以看到驱动LD1的信号波形的占空比从0变到50%,然后又从50%变到0。同样还是利用ST的开发工具,生成一个C语言程序的框架,然后修改其中的main.c,修改后的代码如下。//程序描述:用PWM输出驱动LED#include"STM8S207C_S.h"voidCLK_Init(void);voidTIM_Init(void);//函数功能:延时函数//输入参数:ms--要延时的毫秒数,这里假设CPU的主频为2MHZ//输出参数:无//返回值:无//备{unsignedchari;while(ms!=0){for(i=0;i<250;i++){}for(i=0;i<75;i++){}ms--;}}//函数功能:初始化时钟//输入参数:无//输出参数:无//返回值:无//备注:无注:无voidDelayMS(unsignedintms)

网站上整理的免费分享voidCLK_Init(){CLK_CKDIVR=0x11;//10:fHSI=fHSIRCoutput/4//=16MHZ/4=4MHZ//001:fCPU=fMASTER/2.=2MHZ}//函数功能:初始化定时器2的通道2,用于控制LED的亮度//输入参数:无//输出参数:无//返回值:无//备注:无voidTIM_Init(){TIM2_CCMR2=TIM2_CCMR2|0x70;//OutputmodePWM2.//通道2被设置成比较输出方式//OC2M=111,为PWM模式2,//向上计数时,若计数器小于比较值,为无效电平//即当计数器在0到比较值时,输出为1,否则为0TIM2_CCER1=TIM2_CCER1|0x30;//CCpolaritylow,enablePWMoutput//CC2P=1,低电平为有效电平//CC2E=1,开启输出引脚//初始化自动装载寄存器,决定PWM方波的频率,Fpwm=4000000/256=15625HZTIM2_ARRH=0;TIM2_ARRL=0xFF;//初始化比较寄存器,决定PWM方波的占空比TIM2_CCR2H=0;TIM2_CCR2L=0;//初始化时钟分频器为1,即计数器的时钟频率为Fmaster=4MHZTIM2_PSCR=0;//启动计数TIM2_CR1=TIM2_CR1|0x01;}main()*/

网站上整理的免费分享{unsignedchari;CLK_Init();TIM_Init();while(1){//下面的循环将占空比逐渐从0递增到50%for(i=0;i<128;i++){TIM2_CCR2H=0;TIM2_CCR2L=i;DelayMS(5);}//下面的循环将占空比逐渐从50%递减到0for(i=128;i>0;i--){TIM2_CCR2H=0;TIM2_CCR2L=i;DelayMS(5);}}}//初始化时钟//初始化定时器//进入无限循环