在使用STM8单片机的ADC功能时,读取ADC数据时一般有两种方式,一种是通常不断地读取采样标志位,来判断ADC采样是否结束,一种是通过中断的方式来通知系统采样是否结束。
有时候采样ADC数据的时候,需要按照一定的时间间隔,定点的去采样数据。一般使用的方式就是通过定时器定时,然后在定时中断函数中再去读取ADC采样的数据。但是这种方式采样的时间是不固定的,比如进入定时器中断后,ADC采样刚结束,就可以直接使用当前采样到的数据。但是如果运气不好的话,进入定时中断后,ADC采样刚开始,那么此时就需要等到ADC采样结束后,才能使用数据。这样就好导致每次读取ADC数据时还会有随机的一段等到ADC数据的延时。
通常查阅STM8单片机的手册发现,ADC采样可以通过定时器来触发。
通过定时器来触发ADC采样时,定时器的定时时间是固定的,采样时间也是固定的,这样采样数据的间隔也就是固定的。这样通过定时器来触发ADC的采样时间,就能完全保证每次读取ADC采样数据的时间间隔都是一样的,从而避免了数据的误差。
数据手册中对于开启ADC触发功能描述如下:
对于如何通过代码来设置ADC触发,官方也没有详细的说明,在网上也没有找到相关例程。所以只能自己摸索,还好通过自己的一番摸索,成功的通过定时器的TRGO事件触发了ADC的启动。
关于ADC相关寄存器的设置,基本就是上面说的6条。接下来需要设置的就是定时器的相关寄存器。
关于定时器只需要设置CR2寄存器中的 MMS位就可以了。
接下来就通过代码来实现。
#include "adc.h"
#include "main.h"
#include "led.h"
u16 DATAH = 0; //ADC转换值高8位
u16 DATAL = 0; //ADC转换值低8位
_Bool ADC_flag = 0; //ADC转换成功标志
u16 adc_cnt = 0;
//AD通道引脚初始化
void ADC_GPIO_Init( void )
{
PD_DDR &= ~( 1 << 2 ); //PD2 设置为输入
PD_CR1 &= ~( 1 << 2 ); //PD2 设置为悬空输入
PD_DDR &= ~( 1 << 3 ); //PD3 设置为输入
PD_CR1 &= ~( 1 << 3 ); //PD3 设置为悬空输入
}
//ch 为单片机的ADC 通道
//ADC输入通道初始化入口参数表示通道选择
void ADC_CH_Init( u8 ch )
{
char l = 0;
ADC_CR1 = 0x00; //fADC = fMASTER/2, 8Mhz 单次转换,禁止转换
ADC_CR2 = 0x00; //默认左对齐 读数据时先读高在读低
ADC_CR2 |= ( 1 << 6 ); //外部触发使能
ADC_CSR |= ch; //控制状态寄存器 选择要 AD输入通道 如:PD2(AIN3)
ADC_TDRL = ( 1 << ch ); //禁止相应通道 施密特触发功能 1左移ch位
ADC_CR1 |= 0x01; //使能ADC并开始转换
ADC_CSR |= ( 1 << 5 ); //EOCIE 使能转换结束中断 EOC中断使能
for( l = 0; l < 100; l++ ); //延时,保证ADC模块的上电完成 至少7us
ADC_CR1 = ADC_CR1 | 0x01; //再次将CR1寄存器的最低位置1 使能ADC 并开始转换
}
u16 value = 0;
//AD中断服务函数 中断号22
#pragma vector = 24 // IAR中的中断号,要在STVD中的中断号上加2
__interrupt void ADC_Handle( void )
{
ADC_CSR &= ~0x80; // 转换结束标志位清零 EOC
//默认左对齐 读数据时先读高高8位 再读低8位
DATAH = ADC_DRH; // 读出ADC结果的高8位
DATAL = ADC_DRL; // 读出ADC结果的低8位
ADC_flag = 1; // ADC中断标志 置1
value = ( DATAH << 2 ) + DATAL ; //得到十位精度的数据 0--1024
adc_cnt++;
LED = !LED;
}
在ADC代码中,相比普通的ADC初始化方式,这里只需要增加一句对ADC_CR2寄存器的设置。
ADC_CR2 |= ( 1 << 6 ); //外部触发使能
在ADC_CR2寄存器中 使能外部触发转换功能,设置外部触发事件为 内部定时器1 TRG事件。
这里ADC使用的是单次触发模式,开启外部触发功能,开启ADC转换中断,当ADC转换完成之后,就会进入到ADC中断中,在中断中读取采样的数据,然后翻转LED的状态,通过示波器观察LED引脚电平的变化,就可以知道ADC中断进入的频率了。
接下来编写定时器初始化代码。
unsigned long time_cnt = 0;
// 使用 定时器触发 ADC采样
void tim1_init( void )
{
TIM1_ARRH = ( unsigned char )( 1000 >> 8 ); //定时1ms
TIM1_ARRL = ( unsigned char )( 1000 );
TIM1_PSCRH = ( unsigned char )( 0x0F >> 8 ); // 16M / (1+15) =1M
TIM1_PSCRL = ( unsigned char )( 0x0F );
TIM1_RCR = 0x00; //重复计数器值
TIM1_SR1 = ( ~0x01 ); //清除更新中断标志
TIM1_CR2 |= ( 2 << 4 ); //使能信号,用于触发输出(TRGO)
TIM1_CR1 |= 0x01; //使能计数器
TIM1_IER |= 0x01; //更新中断使能
}
#pragma vector = 13 //IAR中的中断号,要在STVD中的中断号上加2
__interrupt void Timer1_Handle( void ) //1ms 定时中断
{
TIM1_SR1 = ( ~0x01 ); //清除更新中断标志
time_cnt++;
}
定时器的初始化代码,也比正常情况下初始化代码多了一行。
TIM1_CR2 |= ( 2 << 4 ); //使能信号,用于触发输出(TRGO)
用来开启定时的的TRG功能。
经过测试,这里定时器CR2寄存器中的值 只能设置为 010 或者 011,设置为其他值时,不能触发ADC采样。最开始测试的时候按照芯片资料上这个说明,MMS的值设置的是 001,ADC总是触发不了,还以为是方法的问题,最后才发现是MMS值设置的问题。
ADC和定时器初始化代码设置完成之后,接下来在主函数中初始化这两个函数就行了,按照资料上说的,首先初始化完ADC之后,再初始化定时器。
void main( void )
{
__asm( "sim" ); //禁止中断
SysClkInit();
delay_init( 16 );
LED_GPIO_Init();
ADC_GPIO_Init();
ADC_CH_Init(3);
tim1_init();
__asm( "rim" ); //开启中断
while( 1 )
{
}
}
接下来运行程序。
分别在ADC中断中和定时器中断中用一个变量来统计中断执行的次数,通过变量变量观察窗口可以看到,ADC中断的次数比定时器中断的次数多了1次。这是因为ADC在初始化的时候,已经运行了一次。
然后通过示波器观察LED口的电平。
定时器的定时时间是1ms,LED的高低电平时间也是1ms,说明通过定时器触发ADC采样功能是正常运行的。
为了减小系统进入中断的次数,可以将定时器的中断功能关闭掉。定时器中断功能关闭后,ADC的触发功能依然可以正常使用。
这样只需要开启一个ADC中断,再加上定时器的TRG触发功能后,就可以实现ADC定时采样的功能了。
上一篇:STM8单片机通过PWM触发ADC同步采样
下一篇:单片机快速将库函数版代码移植为寄存器代码方法
推荐阅读最新更新时间:2024-11-12 11:43
推荐帖子
- 分享一个threadx在linux上的坑
- threadx跑在linux上面,起因是当时想把中位机(linux)和单片机的代码统一起来,用一套基础os来覆盖控制端和板卡。看着threadx有linux的port,因此搞上去很畅快。但是问题来了,threadx上用的定时器等本来应该是硬件中断产生的信号,在linuxport上使用的软件定时器+信号signal的方式,SIGUSR1和SIGUSR2,可是大量linux的系统调用都会被中断打断,返回EINT,这让很多休眠等待的系统调用,比如阻塞读,不得不处理EINT中断后的恢复。设置信
- freebsder 嵌入式系统
- DIY手机+蓝雨夜 材料准备
- DIY手机+蓝雨夜材料准备这段时间一直在选择器件。先把需要的元器件准备起来,该买的买,该申请的申请主要器件还是选TI的了!再加上以前论坛里团购的片子先把硬件都考虑进去,程序功能等看时间是否允许在完善吧!DIY手机+蓝雨夜材料准备高大上,TI的样片,口水ing。楼主准备的材料不错,期待大作点歌赞,稍后我也为活动做点贡献看到了:模块,2个屏,蓝牙,电源管理相片芯片等sacq发表于2014-5-3014:38看到了:模块,2个屏,蓝牙,电源管理相片芯片
- 蓝雨夜 DIY/开源硬件专区
- MSP430F5438学习笔记 FLAHS操作
- 1.前言MSP430F5438的片内FLASH可以当做EEPROM使用,该部分FLASH称为INFOFLASH,总共有4块每块128Byte。虽然INFOFLASH容量比较少,但是多数情况还是够用的。2.代码实现viewplaincopy//时钟默认情况//FLL时钟FLL选择XT1//辅助时钟ACLK选择XT132768Hz//主系统时钟MCLK选择DCOCLKDIV8000000Hz//子系统时钟SMCLK选
- fish001 微控制器 MCU
- CortexA8+DSP BeagleBoard 板子+扩展板
- 去年十一月份chipsee购买,基本没用过,九成新,运行Android完美,wifi链接完美。原价2700,出收价:2300,可淘宝链接:有诚意的加qq:307799870如有需要,可提供Linux下移植QT4.7+PowerVR的移植过程。------------------------------------------------------------------------------------------------------------
- sblpp 淘e淘
- 有关NRF24L01与ARM板的连接 问题
- 现在准备用NRF24L01无线射频模块,手里还有一块smart210的板子,求大神告知如何将开发板与射频模块正确连接,谢谢,初学者希望您能指导一下,万分感谢有关NRF24L01与ARM板的连接问题2401用的是SPI吧?NRF24L01采用4线SPI通讯,你是要采用软件模拟SPI还是使用内部的SPI通讯模块呢?建议详细阅读一下SPI通讯部分,然后结合网上相关例程进行调试。spi接口,不过很多例子都是io模拟的.这些都是51的处理器,楼主用的啥处理器啊?smart210的板子应该
- Agnoi RF/无线
- msp-fet430UIF郁闷
- 我的笔记本只有4个usb口,以前只能用usb转rs232用编程器开发程序,现在看到了由usb接口的jtagfet十分激动,买了一个,可是软件又不支持v3.20真让我郁闷,现在有的回去用我的msp430prgs-III了。真是不胜感激!!msp-fet430UIF郁闷TI的msp-fet430UIF应该支持IAREW430V3.20A的。光盘里自带了3.21版本的编译器啊,直接安装好了,然后在到安装目录下去找到driver把USB在驱动下就没有问题了,完全可以使用,不过建议
- gtongy 微控制器 MCU
设计资源 培训 开发板 精华推荐
- STM32核心板开发套件(LY-STM32)
- 小车比赛
- LT1933IDCB 3.3V 降压转换器的典型应用电路
- DC1370A-N,LTC2262-12 演示板,12 位,150 Msps ADC,CMOS 输出,5-170MHz
- 【涂鸦智能】物联网温湿度计
- SI5338-EVB,使用 Si5330 系列任意频率、任意输出时钟发生器和时钟缓冲器的评估板
- LT1764ET 并联 LDO 稳压器以实现更高输出电流的典型应用
- ADP172CB1.26-EVALZ,ADP172 1.26V 输出线性稳压器评估板
- AN/01029 12 位高速 ADC,专为视频数据数字化而设计
- 使用 Analog Devices 的 LTC3789EGN#PBF 的参考设计