这几天在研究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_BANKED
extern 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 DEFAULT
static 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.s
void 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
如果有什么意见或建议请留言!
如果使用过程中出现问题了,请再读两遍代码中的注释,还不能解决再来找我。