MC9S12XEP100 ATD模块 驱动程序

来源:互联网 发布:图书馆管理系统 uc矩阵 编辑:程序博客网 时间:2024/05/21 09:02

这几天在研究ATD模块,把芯片手册的ATD模块通读了好几遍,这里是本人对手册的翻译:http://blog.csdn.net/lin_strong/article/details/78286661。本以为完全理解了这个模块,结果一上手才发现还是有很多坑。目前已发现的坑已经写在.c文件里头了。也可能是因为封装模块时测试是用的芯片仿真才导致的那些坑,后面有空的话进行进一步的测试。

自己对ATD模块进行了一个简单的封装。发现网上流传的大部分示例都是使用同步的方式(轮询转换完成标志位)来获得结果,不符合我追求效率的强迫症性格,所以这个模块中,我是使用了异步事件通知的方式实现的。同时由于硬件提供的多通道方法不够灵活,通道间必须连续;于是自己还加了个用软件方式实现的多通道,实现能够跳着通道扫描自己需要的AN端口。

当前版本没有实现外部触发功能,只是封装了转换功能;同时由于目前我在使用uCOS-II,所以直接使用了一些OS提供定义和变量,如果不使用uCOS的话,.c文件中大致讲了下修改的方法。

下面给出源代码。
ATD.h:

/*************************************************************************************************************                                      ATD SUPPORT PACKAGE*                                    Freescale MC9S12XEP100*                                    飞思卡尔单片机ATD支持包** File    : ATD.h* By      : Lin Shijun(http://blog.csdn.net/lin_strong)* Date    : 2017/10/22* Version : V1.0* Note    : 1.Just for convenient,the current version only support right unsigned result.*           2.you should point ATD0's interrupt address(default $FFD2) to ATD0_RxTxISR(see ATD.s).*           3.this implementation don't support external trigger. If you want this feature,some modification*              must be made.*           4.this module take the asynchronous method to inform a successful conversion event.*              Listen to onATDConverted event to get the result.*             be careful,this event is called by ISR, return as soon as possible.*           5.this implementation is based on uC/OS-II,if you don't use this OS,see comment in ATD.c**********************************************************************************************************/#ifndef ATD_MODULE_H#define ATD_MODULE_H/***********************************************************************************************************                                       INCLUDES**********************************************************************************************************/#include <os_cpu.h>#include "MC9S12XEP100.h"/***********************************************************************************************************                                        CONSTANTS**********************************************************************************************************/#ifndef TRUE#define TRUE   1#endif#ifndef FALSE#define FALSE  0#endif#ifndef NULL#define NULL 0x00#endif//typedef byte INT8U;//typedef word INT16U;// ATD通道编号 参数名为portNum的需使用这个宏// Number of ATD channel,use this marco when arugment name is "portNum"#define ATD0_NUM       0#define ATD1_NUM       1#define ATD2_NUM       2#define ATD3_NUM       3#define ATD4_NUM       4#define ATD5_NUM       5#define ATD6_NUM       6#define ATD7_NUM       7#define ATD8_NUM       8#define ATD9_NUM       9#define ATD10_NUM      10#define ATD11_NUM      11#define ATD12_NUM      12#define ATD13_NUM      13#define ATD14_NUM      14#define ATD15_NUM      15#define ATDNULL        0xFF// ATD通道 参数名为ports的需使用这个宏,可以使用位运算多选,如 ATD0_PORT | ATD2_PORT// bit of ATD channel,use this marco when arugment name is "ports", can use '|' to multiselect,e.g ATD0_PORT | ATD2_PORT#define ATD0_PORT      0x0001#define ATD1_PORT      0x0002#define ATD2_PORT      0x0004#define ATD3_PORT      0x0008#define ATD4_PORT      0x0010#define ATD5_PORT      0x0020#define ATD6_PORT      0x0040#define ATD7_PORT      0x0080#define ATD8_PORT      0x0100#define ATD9_PORT      0x0200#define ATD10_PORT     0x0400#define ATD11_PORT     0x0800#define ATD12_PORT     0x1000#define ATD13_PORT     0x2000#define ATD14_PORT     0x4000#define ATD15_PORT     0x8000// INIT option#define ATDINIT_PRECISION_8BIT        0               // 8位精度转换结果#define ATDINIT_PRECISION_10BIT       1#define ATDINIT_PRECISION_12BIT       2#define ATDINIT_SAMPLETIME_4CIRCLE    0               // 4个A/D时钟周期#define ATDINIT_SAMPLETIME_6CIRCLE    1#define ATDINIT_SAMPLETIME_8CIRCLE    2#define ATDINIT_SAMPLETIME_10CIRCLE   3#define ATDINIT_SAMPLETIME_12CIRCLE   4#define ATDINIT_SAMPLETIME_16CIRCLE   5#define ATDINIT_SAMPLETIME_20CIRCLE   6#define ATDINIT_SAMPLETIME_24CIRCLE   7#define ATDINIT_RSPINFRZ_IGNORE       0               // 当背景调试下遇到断点时,忽略它。#define ATDINIT_RSPINFRZ_COMPLETE     2               // 当背景调试下遇到断点时,完成当前转换序列然后再冻结。#define ATDINIT_RSPINFRZ_FREEZE       3               // 当背景调试下遇到断点时,立即冻结。// sample option#define ATD_SAMPLE_MODE_ONESHOT       0x00            // 采样一次#define ATD_SAMPLE_MODE_CONTINUE      0x01            // 连续采样#define ATD_SAMPLE_START_INIDLE       0x00            // 如果在采样过程中调用它,则会忽视采样请求并报错#define ATD_SAMPLE_START_ANYWAY       0x02            // 不管是否在采样中,都立刻重新开始采样// error code #define ATD_ERR_NONE             0x00                 // if no error #define ATD_ERR_INVALIDCHANNEL   0x01                 // if the specified channel is invalid#define ATD_ERR_ARG_OUTOFRANGE   0x02                 // if the arugment is out of range#define ATD_ERR_RUNNING          0x03                 // if want to start sampling when the module is running.#define ATD_ERR_NOTINIT          0x04                 // if the module haven't init.#define ATD_ERR_UNKNOWN          0x05                 // if unknown error happen./***********************************************************************************************************                                      CONFIGURE **********************************************************************************************************/#ifndef BUS_CLOCK  #define BUS_CLOCK     32000000L         // specify the frequency of bus clock,or define it somewhere.#endif#define ATD_PRECISION   ATDINIT_PRECISION_12BIT     // specify the precision of atd result(see ATDINIT_PRECISION_XXXX).#define ATD_SAMPLETIME  ATDINIT_SAMPLETIME_4CIRCLE  // specify the time of a sample (see ATDINIT_SAMPLETIME_XXXX).#define ATD_DISCHARGE   TRUE                        // whether to discharge before start sampling.#define ATD_STOPINWAIT  TRUE                        // whether to stop when in wait mode(will coninue when resume).#define ATD_RSPINFRZ    ATDINIT_RSPINFRZ_COMPLETE   // specify what to do when in freeze mode(see ATDINIT_RSPINFRZ_XXXX).// code generation#define ATD_ARGUMENT_CHECK_EN   TRUE      // whether to add argument check code to each function                                          // NOTE: if TRUE,you can save a few code,however,it become                                           // your responsibility to ensure the validity of arguments.#define ATD_CODE_SAMPLEMULT_EN  TRUE      // TRUE:enable code for ATD_StartSampleMult#define ATD_CODE_SAMPLEREQ_EN   TRUE      // TRUE:enable code for ATD_isSampling#define ATD_CODE_SAMPLESTOP_EN  TRUE      // TRUE:enable code for ATD_StopSample// the maxvalue of different percision.#if(ATD_PRECISION == ATDINIT_PRECISION_8BIT)  #define ATD_MAXVALUE   0xFF#elif(ATD_PRECISION == ATDINIT_PRECISION_10BIT)  #define ATD_MAXVALUE   0x3FF#elif(ATD_PRECISION == ATDINIT_PRECISION_12BIT)  #define ATD_MAXVALUE   0xFFF#else  #error "please select the valid precision."#endif/***********************************************************************************************************                                        EVENT**********************************************************************************************************/typedef void (* ATD_CONVERTED_EVENT)(INT8U portNum,INT16U rst);// 当任何ATD模块转换完成时通过这个函数来进行通知,通过监听这个事件来获得结果。// argument: portNum   是哪个通道的采样结果(e.g ATD0_NUM)//           rst       采样结果(0对应Vrl,ATD_MAXVALUE对应Vrh)extern ATD_CONVERTED_EVENT onATDConverted;/***********************************************************************************************************                                    FUNCTION PROTOTYPE**********************************************************************************************************/// 初始化ATD模块void  ATD_ModuleInit(void);INT8U ATD_StartSampleSingle(INT8U portNum,INT8U option);// 使用这个方法的话,多个通道必须是连续的,但是因为使用硬件方法实现,效率会更高点INT8U ATD_StartSampleScan(INT8U portNumStart,INT8U portsCnt,INT8U option);// 使用这个方式的话,要采样的通道可以跳着采,采样一定是从通道号最小的通道开始的,用软件实现,牺牲了点效率以换取// 更大的灵活性。INT8U ATD_StartSampleMult(INT16U ports,INT8U option);INT8U ATD_isSampling(void);void  ATD_StopSample(void);// 中断处理函数(ATD.s),记得将中断地址指向它#pragma CODE_SEG __NEAR_SEG NON_BANKEDextern void near  ATD0_RxTxISR(void);#pragma CODE_SEG DEFAULT/***********************************************************************************************************                                       ERROR CHECK**********************************************************************************************************/#ifndef ATD_PRECISION#error "your must specify ATD_PRECISION ."#endif#ifndef BUS_CLOCK#error "your must specify BUS_CLOCK ."#endif#endif

ATD.c:

/*************************************************************************************************************                                      ATD SUPPORT PACKAGE*                                    Freescale MC9S12XEP100*                                    飞思卡尔单片机ATD支持包** File    : ATD.c* By      : Lin Shijun(http://blog.csdn.net/lin_strong)* Date    : 2017/10/22* Version : V1.0* Note    : 1.为了更大的灵活性,此模块实际同时使用了ATD模块自身提供的扫描功能(ScanMode)与用软件模拟的扫描功能(BitsMode)*           2.目前已经发现这个模块的问题,不知道是不是只是因为在使用Full Chip Simulation才出现的:*              A. 到达 ATD0CTL0_WRAP 对应的通道后的下一个通道虽然确实是AN0,但是再下一个又回到原来的位置了*                 实际效果就像只是在序列中把 ATD0CTL0_WRAP 的那个通道的后一个替换为了AN0而已,然后该哪个还哪个*                 而不是AN1。*              B. 采样序列数过AN15后并不会返回AN0开始采样,而是直接后面的采样值都是0。(-_-||这和手册中说的不一样)*                 这直接导致了ATD_StartSampleScan函数对应的限制。*              C. 要是使用了自动清零的话,如果中断时不及时取出数据,就会出问题(表现为跳到3E中断地址),所以没有使用*                 自动清零,而是直接手动清零。*           3.如果知道"直接寻址区"怎么使用,可以直接把#pragma DATA_SEG __SHORT_SEG DEFAULT中的DEFAULT改掉,这个*             使用的实际上还是"扩展寻址区"。 以获得更高的效率。*           4.这个实现用到了uC/OS_II嵌入式操作系统,如果不使用uCOS,则主要修改的地方:*              A. 去掉本文件中的  #include <ucos_ii.h>*              B. 把OSUnMapTbl常量前的注释去掉*              C. 删掉ATD.s 并修改 ATD0_ISR_Handler 将其作为ISR(现在它是被ISR调用的函数)。**********************************************************************************************************//***********************************************************************************************************                                       INCLUDES**********************************************************************************************************/#include "ATD.h"#include <ucos_ii.h>/***********************************************************************************************************                                        CONSTANTS**********************************************************************************************************/// Map to ATD register#define ATD0DR(NumInSeq)  (*((INT16U *)(&ATD0DR0) + (NumInSeq)))// ATD_CLOCK = BUS_CLOCK / (2 * PRS + 1)// ATD_CLOCK should between 500kHz and 2MHz.// calculate here to set ATD_CLOCK to 2MHz.#define PRS_VALUE  ((BUS_CLOCK/2000000L - 1) >> 1)// uCos的文件中自带查询表,所以直接用了,要是不使用uCos,可以把下面的注释去掉//INT8U  const  OSUnMapTbl[256] = {//    0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x00 to 0x0F                             *///    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x10 to 0x1F                             *///    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x20 to 0x2F                             *///    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x30 to 0x3F                             *///    6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x40 to 0x4F                             *///    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x50 to 0x5F                             *///    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x60 to 0x6F                             *///    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x70 to 0x7F                             *///    7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x80 to 0x8F                             *///    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x90 to 0x9F                             *///    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xA0 to 0xAF                             *///    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xB0 to 0xBF                             *///    6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xC0 to 0xCF                             *///    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xD0 to 0xDF                             *///    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xE0 to 0xEF                             *///    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0        /* 0xF0 to 0xFF                             *///};// state#define ATD_STATE_NOINIT         0#define ATD_STATE_READY          1#define ATD_STATE_SHOTTING       2#define ATD_STATE_SCANNING       3#define ATD_STATE_SHOTTING_BIT   4#define ATD_STATE_SCANNING_BIT   5// mask#define ATD_SAMPLE_MASK_MODE    0x01#define ATD_SAMPLE_MASK_START   0x02/***********************************************************************************************************                                      PUBLIC VARIABLE 公开变量**********************************************************************************************************/// 转换完成事件ATD_CONVERTED_EVENT onATDConverted = NULL;/***********************************************************************************************************                                      LOCAL VARIABLE 本地变量**********************************************************************************************************/typedef union {  struct {    INT16U BitSequence;      } BitsMode;  struct {    INT8U PortNum;    INT8U PortsCnt;  } ScanMode;} ATD_STRUCT;#pragma DATA_SEG __SHORT_SEG DEFAULTstatic ATD_STRUCT _SeqTotal;#if (ATD_CODE_SAMPLEMULT_EN == TRUE)static ATD_STRUCT _SeqLeft;#endif#define _PortNumStart _SeqTotal.ScanMode.PortNum       // the portnum that sample sequence start.          (ScanMode)#define _PortsCnt     _SeqTotal.ScanMode.PortsCnt      // the number of ports to convert in sequence.      (ScanMode)//以下两个可以在用ISR中用局部函数来代替,所以实际上没有用它#define _PortNumNow   _SeqLeft.ScanMode.PortNum        // the next portnum that will get the rst.          (ScanMode)#define _PortsCntLeft _SeqLeft.ScanMode.PortsCnt       // the number of ports wait to convert in sequence. (ScanMode)#define _PortsSeq     _SeqTotal.BitsMode.BitSequence   // the sample sequence.                             (BitsMode)#define _PortsSeqLeft _SeqLeft.BitsMode.BitSequence    // the sample sequence wait to convert.             (BitsMode)static INT8U  ATDState = ATD_STATE_NOINIT; // the state of ATD module(see ATD_STATE_XXXX ).#pragma DATA_SEG DEFAULT/***********************************************************************************************************                                  LOCAL VARIABLE DECLARE**********************************************************************************************************/// 初始化状态值static void _ATD_InitialState(void);// 内部使用返回一组通道中最小的那个的编号,应保证参数ports不等于0,否则得到错误的结果static INT8U _ATD_PortsToNextNum(INT16U ports);/***********************************************************************************************************                                     ATD_ModuleInit()** Description : This function is used to initialize the ATD module.you should call this function before *                 using anyother function in ATD module.** Return      :  * Notes       : **********************************************************************************************************/void ATD_ModuleInit(void){   ATD0CTL2 = 0x02 | (ATD_STOPINWAIT << 5) ;  //启动A/D模块,不快速清零(快速清零在扫描模式下可能会出问题),使能中断      ATD0CTL1_SRES = ATD_PRECISION;  // 设置转换精度   #if(ATD_DISCHARGE == TRUE)      ATD0CTL1_SMP_DIS = 1;   #endif    // 转换结果右对齐,预设为只转换一个通道,设置冻结模式下的响应,非FIFO模式   ATD0CTL3 = ATD0CTL3_DJM_MASK | ATD0CTL3_S1C_MASK | (ATD_RSPINFRZ & 0x03);     ATD0CTL4_SMP = ATD_SAMPLETIME;             // 设置采样时间的ATD时钟周期个数   ATD0CTL4_PRS = PRS_VALUE;                  // 预分频AD模块时钟为2MHz   _ATD_InitialState();                   // 初始化状态}/***********************************************************************************************************                                    ATD_StartSampleSingle()** Description : This function is used to start the sample process of one ATD channel.* Arguments   : portNum   the ATD channel to start sampling (e.g ATD1)*               option    the option of the sample(see ATD_SAMPLE_XXXX_XXXX). bit mode:*                          for example: *                              ATD_SAMPLE_ONESHOT | ATD_SAMPLE_START_ANYWAY   *                              for sample one shot and restart sampling even if ATD module is sampling.* Return      :  ATD_ERR_NONE            if no error.*                ATD_ERR_INVALIDCHANNEL  if the channel is invalid.*                ATD_ERR_RUNNING         if the ATD is sampling and not set ATD_SAMPLE_START_ANYWAY*                ATD_ERR_NOTINIT         if the module haven't init.*                ATD_ERR_UNKNOWN         if unknown error happen.* Notes       : **********************************************************************************************************/INT8U ATD_StartSampleSingle(INT8U portNum,INT8U option){    return ATD_StartSampleScan(portNum,1,option);}/***********************************************************************************************************                                    ATD_StartSampleScan()** Description : This function is used to start the sample sequence of ATD channels.* Arguments   : portNumStart the first channel in sequence for conversion (e.g ATD4 means start from Channel 4)*               portsCnt     the number of channels that will sample,count from the portStart. value between 1 and 16.*               option       the option of the sample(see ATD_SAMPLE_XXXX_XXXX). bit mode:*                              for example: *                                ATD_SAMPLE_ONESHOT | ATD_SAMPLE_START_ANYWAY   *                                for sample one shot and restart sampling even if ATD module is sampling.* Return      :  ATD_ERR_NONE            if no error.*                ATD_ERR_INVALIDCHANNEL  if the channel is invalid.*                ATD_ERR_ARG_OUTOFRANGE  if the arugment is out of range*                ATD_ERR_RUNNING         if the ATD is sampling and not set ATD_SAMPLE_START_ANYWAY*                ATD_ERR_NOTINIT         if the module haven't init.*                ATD_ERR_UNKNOWN         if unknown error happen.* Notes       :  when portsCnt is set to 1,is equal to call ATD_StartSampleSingle.*                For hardware reason, portNumStart + portsCnt must not bigger than ATD15.*                for example. if portStart is ATD4_NUM,and portsCnt is 3, then *                    channel ATD4,ATD5,ATD6 will start to sample in order.**********************************************************************************************************/INT8U ATD_StartSampleScan(INT8U portNumStart,INT8U portsCnt,INT8U option){    INT8U rvalue = portNumStart;#if (ATD_ARGUMENT_CHECK_EN == TRUE)    if(portsCnt == 0 || portsCnt > 16)       return ATD_ERR_ARG_OUTOFRANGE;    if(portNumStart + portsCnt> ATD15_NUM)       return ATD_ERR_INVALIDCHANNEL;#endif    switch(ATDState){       case ATD_STATE_NOINIT:         return ATD_ERR_NOTINIT;       case ATD_STATE_SHOTTING:       case ATD_STATE_SCANNING:#if (ATD_CODE_SAMPLEMULT_EN == TRUE)       case ATD_STATE_SHOTTING_BIT:       case ATD_STATE_SCANNING_BIT:#endif         if( (option & ATD_SAMPLE_MASK_START ) ==  ATD_SAMPLE_START_INIDLE)            return ATD_ERR_RUNNING;         // intensively no break; break with the next case;       case ATD_STATE_READY:         break;       default:         return ATD_ERR_UNKNOWN;    }    if( (option & ATD_SAMPLE_MASK_MODE) == ATD_SAMPLE_MODE_CONTINUE){       rvalue |= ATD0CTL5_SCAN_MASK;       ATDState = ATD_STATE_SCANNING;    }else{       ATDState = ATD_STATE_SHOTTING;    }    if( portsCnt > 1 ){       rvalue |= ATD0CTL5_MULT_MASK;       ATD0CTL3 &= 0x87;     // 清除 SnC 位       ATD0CTL3 |= ((portsCnt & 0x0F) << 3);  // 设置SnC位为转换序列的长度    }           _PortNumStart = portNumStart;    _PortsCnt = portsCnt;    ATD0CTL5 = rvalue;     // start sample.    return ATD_ERR_NONE;}/***********************************************************************************************************                                    ATD_StartSampleScan()** Description : This function is used to start the sample sequence of ATD channels.* Arguments   : ports    the ports that will sample (in other word,the sample sequence). e.g ATD0_PORT | ATD2_PORT *               option   the option of the sample(see ATD_SAMPLE_XXXX_XXXX). bit mode:*                          for example: *                              ATD_SAMPLE_ONESHOT | ATD_SAMPLE_START_ANYWAY   *                              for sample one shot and restart sampling even if ATD module is sampling.* Return      :  ATD_ERR_NONE            if no error.*                ATD_ERR_INVALIDCHANNEL  if the channel is invalid.*                ATD_ERR_ARG_OUTOFRANGE  if the arugment is out of range*                ATD_ERR_RUNNING         if the ATD is sampling and not set ATD_SAMPLE_START_ANYWAY*                ATD_ERR_NOTINIT         if the module haven't init.*                ATD_ERR_UNKNOWN         if unknown error happen.* Notes       :  when portsCnt is set to 1,is equal to call ATD_StartSampleSingle.*                when count to ATD16,the next one will turn around to ATD1.*                for example. if portStart is ATD16,and portsCnt is 3, then *                    channel ATD16,ATD1,ATD2 will start to sample in order.**********************************************************************************************************/#if (ATD_CODE_SAMPLEMULT_EN == TRUE)INT8U ATD_StartSampleMult(INT16U ports,INT8U option){#if (ATD_ARGUMENT_CHECK_EN == TRUE)    if(ports == 0)       return ATD_ERR_INVALIDCHANNEL;#endif    switch(ATDState){       case ATD_STATE_NOINIT:         return ATD_ERR_NOTINIT;       case ATD_STATE_SHOTTING: case ATD_STATE_SCANNING:case ATD_STATE_SHOTTING_BIT:case ATD_STATE_SCANNING_BIT:         if( (option & ATD_SAMPLE_MASK_START ) ==  ATD_SAMPLE_START_INIDLE)            return ATD_ERR_RUNNING;         // intensively no break; break with the next case;       case ATD_STATE_READY:         break;       default:         return ATD_ERR_UNKNOWN;    }    _PortsSeq = _PortsSeqLeft =  ports;    // save seq    if( (option & ATD_SAMPLE_MASK_MODE) == ATD_SAMPLE_MODE_CONTINUE){     // save state       ATDState = ATD_STATE_SCANNING_BIT;    }else{       ATDState = ATD_STATE_SHOTTING_BIT;    }    ATD0CTL5 = _ATD_PortsToNextNum(ports); // start sample, no scan,single channel,only sample the next channel.    return ATD_ERR_NONE;}#endif/***********************************************************************************************************                                     ATD_isSampling()** Description : use this function to ask whether ATD module is sampling.** Return      : TRUE    ATD module is sampling.*               FALSE   ATD module is not sampling.* Notes       : this function will stop the sampling whatever the state is now.**********************************************************************************************************/#if (ATD_CODE_SAMPLEREQ_EN == TRUE)INT8U ATD_isSampling(void){    return (ATDState > 1); // && ATDState < 6); }#endif/***********************************************************************************************************                                     ATD_StopSample()** Description : This function is used to stop the sampling of ATD module.** Return      : * Notes       : this function will stop the sampling whatever the state is now.**********************************************************************************************************/#if (ATD_CODE_SAMPLESTOP_EN == TRUE)void ATD_StopSample(void){    volatile INT8U temp;    if(ATDState == ATD_STATE_NOINIT)       return;    temp = ATD0CTL0;    ATD0CTL0 = temp;       // 通过写入CTL0寄存器来停止当前采样序列    _ATD_InitialState();}#endif// only used by ATD.svoid ATD0_ISR_Handler (void)  {   volatile INT16U rst;   INT8U curNum,totalNum, portNumNow;   // 在FIFO模式下可能第一个结果不在结果寄存器0中,但模块内没用到FIFO所以不会有问题   curNum = 0;   switch(ATDState){    case ATD_STATE_SHOTTING:    case ATD_STATE_SCANNING:       totalNum = _PortsCnt;               // 不管多通道还是单通道,都是整个序列转换完后才会触发中断,所有一次性读取所有的       portNumNow = _PortNumStart;         // 这种情况下,就是从序列起点开始的。       break;#if (ATD_CODE_SAMPLEMULT_EN == TRUE)    case ATD_STATE_SHOTTING_BIT:    case ATD_STATE_SCANNING_BIT:       totalNum = 1;                       // 软件模拟的情况下,每次只转换1个通道。       portNumNow = _ATD_PortsToNextNum(_PortsSeqLeft);     // 当前通道是等待转换的中最右边的bit。       break;#endif    default:                               // 应该是不会进到这里的       totalNum = 0;       break;   }   // 取所有结果并通过事件通知   while(curNum < totalNum){      rst = ATD0DR(curNum++);               // 哪怕没有事件可通知也一定要把数值取出来,所以rst要加volatile避免被优化掉      // 事件通知      if(onATDConverted != NULL)         onATDConverted(portNumNow++,rst);      if(portNumNow > ATD15_NUM)                // 到了ATD15后循环回AN0         portNumNow = ATD0_NUM;   }   // 更新状态及可能重新开始序列   switch(ATDState){    // 用硬件转换的时候直接,    case ATD_STATE_SHOTTING:       _ATD_InitialState();    // 单次转换下,直接初始化状态即可    case ATD_STATE_SCANNING:                               // 序列完成事件       break;#if (ATD_CODE_SAMPLEMULT_EN == TRUE)    case ATD_STATE_SHOTTING_BIT:    case ATD_STATE_SCANNING_BIT:       _PortsSeqLeft &= _PortsSeqLeft - 1; // 清除最后一个1       if(_PortsSeqLeft == 0){                               // 序列完成事件           if(ATDState == ATD_STATE_SHOTTING_BIT){              _ATD_InitialState();         // 单次转换下,直接初始化状态即可              break;           }else{     // if(ATDState == ATD_STATE_SCANNING_BIT)              _PortsSeqLeft = _PortsSeq;   // 连续模式下,重置序列并重新开始转换           }       }       ATD0CTL5 = _ATD_PortsToNextNum(_PortsSeqLeft);       return;#endif    default:                               // 应该是不会进到这里的       break;   }   // 清零标志位   ATD0STAT2 = 0xFFFF;        ATD0STAT0_SCF = 1;} /***********************************************************************************************************                                      LOCAL FUNCTION**********************************************************************************************************/static void _ATD_InitialState(void){   _PortsSeq = 0;#if (ATD_CODE_SAMPLEMULT_EN == TRUE)   _PortsSeqLeft = 0;#endif   ATDState = ATD_STATE_READY; // the state of ATD module(see ATD_STATE_XXXX ). }#if (ATD_CODE_SAMPLEMULT_EN == TRUE)static INT8U _ATD_PortsToNextNum(INT16U ports){   return (ports & 0x00FF)?(OSUnMapTbl[ports & 0x00FF]):(8 + OSUnMapTbl[ports >> 8]);}#endif/***********************************************************************************************************                                       ERROR CHECK**********************************************************************************************************/#if((BUS_CLOCK / (2 * PRS_VALUE + 1)) < 500000L || (BUS_CLOCK / (2 * PRS_VALUE + 1)) > 2200000L)  #error "find some problem in calculating PRS value." PRS_VALUE#endif

ATD.s

;********************************************************************************************************;                                               uC/OS-II;                                         The Real-Time Kernel;;                         (c) Copyright 2002, Jean J. Labrosse, Weston, FL;                                          All Rights Reserved;;;                                       PAGED S12X Specific code;                                            (CODEWARRIOR);; File         : ATD.s; By           : Lin Shijun;; Notes        : THIS FILE *MUST* BE LINKED INTO NON_BANKED MEMORY! 这个文件必须放在非分页内存中;                    modified according to uC/OS-II's example.  依据uC/OS-II的模版修改。;********************************************************************************************************NON_BANKED:       section;********************************************************************************************************;                                          I/O PORT ADDRESSES   I/O口地址;********************************************************************************************************PPAGE:            equ    $0015         ; Addres of PPAGE register (assuming MC9S12XEP100 part)RPAGE:            equ    $0016         ; Addres of RPAGE register (assuming MC9S12XEP100 part)EPAGE:            equ    $0017         ; Addres of EPAGE register (assuming MC9S12XEP100 part)GPAGE:            equ    $0010         ; Addres of GPAGE register (assuming MC9S12XEP100 part);********************************************************************************************************;                                          PUBLIC DECLARATIONS   公开声明;********************************************************************************************************    xdef   ATD0_RxTxISR;********************************************************************************************************;                                         EXTERNAL DECLARATIONS  外部声明;********************************************************************************************************    xref   OSIntExit    xref   OSIntNesting    xref   OSTCBCur    xref   ATD0_ISR_Handler;********************************************************************************************************;                                           ATD RxTx ISR;; Description : This routine is the uC/Probe RxTx interrupt service routine;; Arguments   : none;; Notes       : 1) All USER interrupts should be modeled EXACTLY like this where the only;                  line to be modified is the call to your ISR_Handler and perhaps the call to;                  the label name ATD0_ISR_Handler.;********************************************************************************************************ATD0_RxTxISR:    ldaa   GPAGE                       ; Get current value of GPAGE register    psha                               ; Push GPAGE register onto current task's stack    ldaa   EPAGE                       ; Get current value of EPAGE register    psha                               ; Push EPAGE register onto current task's stack    ldaa   RPAGE                       ; Get current value of RPAGE register    psha                               ; Push RPAGE register onto current task's stack    ldaa   PPAGE                       ; Get current value of PPAGE register    psha                               ; Push PPAGE register onto current task's stack    inc    OSIntNesting                ; Notify uC/OS-II about ISR    ldab   OSIntNesting                ; if (OSIntNesting == 1) {    cmpb   #$01    bne    ATD0_RxTxISR1    ldy    OSTCBCur                    ;     OSTCBCur->OSTCBStkPtr = Stack Pointer    sts    0,y                         ; }ATD0_RxTxISR1:    call   ATD0_ISR_Handler  ; Call TxRx ISR handler. (See ATD.c);   cli                               ; Optionally enable interrupts to allow interrupt nesting    call   OSIntExit                  ; Notify uC/OS-II about end of ISR, a context switch may occur from within OSIntExit().    pula                               ; Get value of PPAGE register    staa   PPAGE                       ; Store into CPU's PPAGE register    pula                               ; Get value of RPAGE register    staa   RPAGE                       ; Store into CPU's RPAGE register    pula                               ; Get value of EPAGE register    staa   EPAGE                       ; Store into CPU's EPAGE register    pula                               ; Get value of GPAGE register    staa   GPAGE                       ; Store into CPU's GPAGE register    rti                               ; Return from interrupt to interrupted task.

下面简单介绍下这个模块的使用:

ATD的转换结果是个无符号数,这个数的位数由ATD.h中的ATD_PRECISION决定,0对应着Vrl的电平,大部分情况下是接地的,即为0V,最大值(ATD_MAXVALUE)则对应着Vrh这个引脚的电平,然后之间的值被平均分配,所以位数越大当然精度越高。

通过提供的
ATD_StartSampleSingle
ATD_StartSampleScan
ATD_StartSampleMult
这几个方法来开始一趟或无数趟转换。其中Mult可以跳着通道转换,而Scan着必须是连续的几个通道比如从AN4到AN8这样子转换。具体看函数注释。

转换的结果会通过onATDConverted这个事件来通知用户,用户需要先告知回调函数。然后在每个通道转换成功后,模块都会回调这个函数,并将转换的是哪个通道,以及它对应的值传递给用户,底层细节已经屏蔽掉了。然后用户就可以在这个事件里使用转换结果干自己想干的事情了。

示例代码:

……static void onATDgetResult(INT8U portNum,INT16U rst);……static  void  AppTaskStart (void *p_arg){    ……    // 初始化ATD模块    ATD_ModuleInit();    // 设置事件回调函数    onATDConverted = &onATDgetResult;    // 连续采样从AN0起的两个通道,即AN0、AN1    ATD_StartSampleScan(ATD0_NUM,2,ATD_SAMPLE_MODE_CONTINUE | ATD_SAMPLE_START_ANYWAY);    // 采样一次AN14的值    // ATD_StartSampleSingle(ATD14_NUM,ATD_SAMPLE_MODE_ONESHOT | ATD_SAMPLE_START_ANYWAY);    // 连续按照 AN0、AN3、AN5、AN9 这个顺序采样    // ATD_StartSampleMult(ATD0_PORT | ATD3_PORT | ATD5_PORT | ATD9_PORT, ATD_SAMPLE_MODE_CONTINUE | ATD_SAMPLE_START_ANYWAY);    while (DEF_TRUE)     {       OSTimeDlyHMSM(0,0,1,500);    }}volatile INT16U result[] = {    0,0};static void onATDgetResult(INT8U portNum,INT16U rst){   result[portNum] = rst;}……

然后在真实芯片上的采样测试:
测试结果

比如对于AN0,假设当前我单片机的Vrl = 0V,Vrh = 5V,精度为12位 (2^12 = 4096)。那么它的电平则为:
5.0 / 4096 * 3699 ≈ 4.515V

如果有什么意见或建议请留言!
如果使用过程中出现问题了,请再读两遍代码中的注释,还不能解决再来找我。

原创粉丝点击