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
如果有什么意见或建议请留言!
如果使用过程中出现问题了,请再读两遍代码中的注释,还不能解决再来找我。
- MC9S12XEP100 ATD模块 驱动程序
- MC9S12XEP100的ATD模块(ADC12B16CV1)
- MC9S12XEP100 SCI(UART)驱动程序
- MC9S12XEP100 SCI(UART)驱动程序2
- 飞思卡尔ATD模块
- HCS12XEP100 ATD模块多通道采样
- HCS12XEP100 ATD模块单通道多次采样
- HCS12XEP100 ATD模块定时中断采样
- ATD 配置
- 基本scull模块驱动程序
- RC522 模块驱动程序
- 实现内核驱动程序模块
- RC522 模块驱动程序
- linux内核模块和驱动程序
- Linux驱动程序的模块参数
- Linux驱动程序模块编译流程
- hx711模块原理图及驱动程序
- Eclipse 删除ATD
- 深入浅出TensorFlow(六)TensorFlow高层封装
- SPRING MVC4 + REST 前后端分离 整理{未完成}
- BZOJ 2186 [Sdoi2008]沙拉公主的困惑
- STM32如何在LCD液晶屏上显示变量的数值
- Vim 快捷键一览表
- MC9S12XEP100 ATD模块 驱动程序
- Word2Vec详解
- 多条件查询分页页面JSP
- JBOSS引入外部jar包报错
- Permission denied (publickey). fatal: Could not read from remote repository. Pleasemake sure you h
- Linux上安装jdk
- ajax使用,前端与后端交互
- 数据探索之数据预处理
- CSS3 Flex布局(容器)