-
- #include "nuc1xx.h"
- #include "NUC1xxM051SeriesCfg.h"
- #include <look.h>
- #include <look/instantiate>
- LOOK_HWINIT()
- {
- // 初始化系统时钟
- UNLOCKREG(0); // 解锁
- SYSCLKs.PWRCON.Bits.XTL12M_EN = 1; // 外部晶振使能
- volatile uintptr_t n = 10000;
- while (--n); // 延时等待晶振稳定
- SYSCLKs.PLLCON.Regs &= ~((1 << SYSCLK_PLLCON_PD)
- | (1 << SYSCLK_PLLCON_OE)); // PLL输出使能
- n = 10000;
- while (--n); // 延时等待PLL稳定
- SYSCLKs.CLKSEL0.Bits.HCLK_S = 2; // 选择CPU时钟为PLL
- SYSCLKs.CLKSEL1.Bits.UART_S = 0; // 选择UART时钟为外部晶振
- SYSCLKs.APBCLK.Regs = (1 << SYSCLK_APBCLK_UART0_EN) // UART0时钟使能
- | (1 << SYSCLK_APBCLK_UART1_EN); // UART1时钟使能
- // 防抖设置
- AHBs.AHB_GPIO.DBNCECON.Regs = (7 << GPIO_DBNCECON_DBCLKSEL)
- | (1 << GPIO_DBNCECON_DBCLKSRC)
- | (1 << GPIO_DBNCECON_ICLK_ON);
- // 关闭蜂鸣器
- GPIOBs.PMD.Bits.PMD10 = GPIO_PMD_OUTPUT;
- GPIOBs.DOUT.Bits.Pin10 = 0;
- }
- // owou_t 类为应用层提供了1-wire总线的部分操作方法(成员函数),并且封装了一些操作内部使用的数据。
- class owou_t : public interrupt_t {
- public:
- owou_t() __OPT_ATTR__;
- bool reset();
- int read(uintptr_t bits = 8) __OPT_ATTR__;
- int write(uintptr_t data, uintptr_t bits = 8) __OPT_ATTR__;
- protected:
- bool isr(int vector);
- void dsr(int vector, uintptr_t count);
- int touch(uintptr_t data, uintptr_t bits);
- private:
- sem_t sem; // 信号灯为 1-wire 操作提供同步
- uint8_t in_data; // 从 1-wire 总线上读出的数据。
- uint8_t out_data; // 要写到 1-wire 总线上的数据,有效的位数由 op_count 设定。
- uint8_t op_count; // 定义域:1 <= x <= 8:out_data 的有效数据位。
- // 0x80:表示此次操作为 1-wire 总线复位。
- };
- // owou_t 构造函数
- __OPT_INLINE__ owou_t:wou_t()
- : sem(0)
- {
- SYSs.GPBMFP.Regs |= (1 << GCR_GPBMFP_UART1_RX)
- | (1 << GCR_GPBMFP_UART1_TX); // 设置 UART1 引脚功能
- attach(UART1_IRQn); // 挂接中断对象
- vector_t::enable(UART1_IRQn); // 允许 UART1_IRQn
- }
- // 1-wire 复位操作
- bool owou_t::reset()
- {
- op_count = 0x80; // 设置 1-wire 总线操作为“复位”
- // 设置 UART1 参数,以产生合适的 1-wire 总线复位操作时序。
- SYSs.IPRSTC2.Bits.UART1_RST = 1;
- SYSs.IPRSTC2.Bits.UART1_RST = 0;
- UART1s.FCR.Regs |= (1 << UART_FCR_TFR) | (1 << UART_FCR_RFR);
- UART1s.LCR.Regs = 3 << UART_LCR_WLS; // 设置线控
- UART1s.IER.Regs = 1 << UART_IER_RDA_IEN; // 允许接收中断
- UART1s.BAUD.Regs = (0x4e0 << UART_BAUD_BRD)
- | (1 << UART_BAUD_DIV_X_ONE)
- | (1 << UART_BAUD_DIV_X_EN); // 波特率 = 9600
- UART1s.DATA.Regs = 0xf0; // 发出“复位”信号
- if (sem.wait(LOOK_TICKS_PER_SEC / 20)) { // 等待“复位”应答
- if (in_data != 0xf0) { // “复位”应答出现
- // 收到复位应答后,重新设置 UART1 参数,用以产生合适的 1-wire 总线读写操作时序。
- SYSs.IPRSTC2.Bits.UART1_RST = 1;
- SYSs.IPRSTC2.Bits.UART1_RST = 0;
- UART1s.FCR.Regs |= (1 << UART_FCR_TFR) | (1 << UART_FCR_RFR);
- UART1s.LCR.Regs = 3 << UART_LCR_WLS;
- UART1s.IER.Regs = 1 << UART_IER_RDA_IEN;
- UART1s.BAUD.Regs = (0x66 << UART_BAUD_BRD)
- | (1 << UART_BAUD_DIV_X_ONE)
- | (1 << UART_BAUD_DIV_X_EN); // 波特率 = 115200
- return true;
- }
- }
- return false;
- }
- // 1-wire 读操作
- // 参数:
- // bits:需要读出的数据位数。定义域:1 <= x <= 8
- // 返回:
- // 读到的数据。
- __OPT_INLINE__ int owou_t::read(uintptr_t bits)
- {
- int data = touch(0xff, bits);
- if (bits < 8)
- data >>= 8 - bits;
- return data;
- }
- // 1-wire 写操作
- // 参数:
- // data: 需要写到 1-wire 总线的数据。
- // bits:数据位数。定义域:1 <= x <= 8
- __OPT_INLINE__ int owou_t::write(uintptr_t data, uintptr_t bits)
- {
- return touch(data, bits);
- }
- // 1-wire 中断服务例程
- // 当复位操作完成或读写1位完成后,中断发生。
- bool owou_t::isr(int vector)
- {
- uintptr_t data = UART1s.DATA.Regs;
- if ((op_count & 0x80) != 0) { // 复位操作
- in_data = data; // 保存接收的数据
- } else { // 读写操作
- in_data = ((data << 8) + in_data) >> 1; // 组合读出的数据
- uintptr_t count = op_count;
- if (--count != 0) {
- op_count = count;
- UART1s.DATA.Regs = 0 - (out_data & 1); // 输出 out_data 最低位(扩展为字节)。
- out_data >>= 1;
- return false;
- }
- }
- return true;
- }
- // 1-wire 中断滞后服务例程
- // 当 isr() 返回 true 后(复位操作完成或所有的数据位读写操作完成),dsr() 被调用。
- void owou_t::dsr(int vector, uintptr_t count)
- {
- sem.do_post(); // 释放信号灯资源。
- }
- // 1-wire 读写例程
- int owou_t::touch(uintptr_t data, uintptr_t bits)
- {
- in_data = 0;
- out_data = data >> 1;
- op_count = bits;
- UART1s.DATA.Regs = 0 - (data & 1); // 写首位
- if (sem.wait(LOOK_TICKS_PER_SEC / 20)) // 等待剩余数据位全部完成
- return in_data;
- return -1;
- }
- owou_t owou; // 创建 1-wire 对象
- // uart0_t 类为应用层提供了简单的 uart 同步输出功能。
- class uart0_t : public interrupt_t {
- public:
- uart0_t() __OPT_ATTR__;
- void puts(const char* str);
- protected:
- bool isr(int vector);
- void dsr(int vector, uintptr_t count);
- private:
- void fillfifo(const char* str);
- private:
- const char* buffer; // 输出缓冲区
- task_t* task; // 正在输出的任务
- };
- // uart0 构造函数
- __OPT_INLINE__ uart0_t::uart0_t()
- {
- SYSs.GPBMFP.Regs |= (1 << GCR_GPBMFP_UART0_RX) | (1 << GCR_GPBMFP_UART0_TX);
- attach(UART0_IRQn);
- vector_t::enable(UART0_IRQn);
- SYSs.IPRSTC2.Bits.UART0_RST = 1;
- SYSs.IPRSTC2.Bits.UART0_RST = 0;
- UART0s.FCR.Regs |= (1 << UART_FCR_TFR) | (1 << UART_FCR_RFR);
- UART0s.LCR.Regs = 3 << UART_LCR_WLS; // 8bits
- UART0s.BAUD.Regs = (0x66 << UART_BAUD_BRD)
- | (1 << UART_BAUD_DIV_X_ONE)
- | (1 << UART_BAUD_DIV_X_EN); // 115200
- }
- // uart0 输出
- void uart0_t::puts(const char* str)
- {
- fillfifo(str); // 填充 fifo
- UART0s.IER.Bits.THRE_IEN = 1; // 允许发送中断
- task = &scheduler.get_current_task();
- delay(); // 阻塞任务
- }
- // uart0 中断服务例程
- bool uart0_t::isr(int vector)
- {
- const char* str = buffer;
- if (str == 0) { // 无数据
- UART0s.IER.Bits.THRE_IEN = 0; // 禁止发送中断
- return true;
- }
- fillfifo(str); // 填充 fifo
- return false;
- }
- // uart0 中断滞后服务例程
- // 所有数据发送完成后,dsr() 被调用
- void uart0_t::dsr(int vector, uintptr_t count)
- {
- task->do_wakeup(); // 唤醒任务
- }
- // uart0 填充 fifo
- void uart0_t::fillfifo(const char* str)
- {
- do {
- char ch;
- ch = *str++;
- if (ch == 0) {
- str = 0;
- break;
- }
- UART0s.DATA.Regs = ch;
- } while (!UART0s.FSR.Bits.TX_FULL);
- buffer = str;
- }
- uart0_t uart0; // 创建 uart0 对象
- mbox_t<int> mbox(0); // 创建 int 型邮箱,初值 0
- // 主任务
- class task_main_t : public task_t {
- public:
- task_main_t() __OPT_ATTR__;
- protected:
- void routine() __attribute__((noreturn));
- private:
- uint8_t buffer[12]; // 1-wire 输入数据缓冲
- char str[28]; // uart0 输出数据缓冲
- };
- __OPT_INLINE__ task_main_t::task_main_t()
- {
- }
- // 主任务例程
- void task_main_t::routine()
- {
- while (true) {
- int msg = mbox.get(); // 等待邮箱消息
- if (msg != 0) {
- if (owou.reset()) {
- do {
- int n;
- if (msg == 1) { // 消息 1,读 ds18b20 ROM
- owou.write(0x33);
- n = 8; // 读 8 字节数据
- } else {
- owou.write(0xcc);
- owou.write(0x44);
- delay(LOOK_TICKS_PER_SEC * 0.75);
- if (!owou.reset()) {
- uart0.puts("reset error\n");
- break;
- }
- owou.write(0xcc);
- owou.write(0xbe);
- n = 9; // 读 9 字节数据
- }
- // 读数据并进行 crc 校验
- uintptr_t crc = 0;
- int i = 0;
- do {
- int data = owou.read();
- buffer = data;
- crc ^= data;
- for (int x = 0; x < 8; x++) {
- uintptr_t tmp = crc;
- crc >>= 1;
- if (tmp & 1)
- crc ^= 0x8c;
- }
- } while (++i < n);
- if (crc == 0) { // crc 正确
- char* p;
- if (msg == 1) { // 消息 1,读 ds18b20 ROM
- // 转换并输出 serial number 信息到 uart0
- uart0.puts("serial number:");
- p = str;
- i = 0;
- do {
- *p++ = ' ';
- int data = buffer;
- int tmp = data >> 4;
- if (tmp < 10)
- tmp += '0';
- else
- tmp = tmp - 10 + 'A';
- *p++ = tmp;
- data &= 0xf;
- if (data < 10)
- data += '0';
- else
- data = data - 10 + 'A';
- *p++ = data;
- } while (++i < 8);
- *p++ = '\n';
- *p = 0;
- p = str;
- } else { // 消息 2,读 ds18b20 scratch pad
- // 转换并输出 temperature 信息到 uart0
- uart0.puts("temperature:");
- int data = *reinterpret_cast<int16_t*>(&buffer[0]);
- bool neg = false;
- if (data < 0) {
- data = -data;
- neg = true;
- }
- p = &str[28];
- *--p = 0;
- *--p = '\n';
- uintptr_t tmp = (data & 0xf) * 625;
- if (tmp != 0) {
- int n = 4;
- bool b = false;
- do {
- uintptr_t rem = tmp;
- tmp = tmp * (((1 << 19) + 9) / 10) >> 19;
- rem -= tmp * 10;
- if (b || rem) {
- *--p = '0' + rem;
- b = true;
- }
- } while (--n);
- *--p = '.';
- }
- data >>= 4;
- do {
- uintptr_t rem = data;
- data = data * (((1 << 11) + 9) / 10) >> 11;
- rem -= data * 10;
- *--p = '0' + rem;
- } while (data);
- if (neg)
- *--p = '-';
- }
- uart0.puts(p); // 输出信息
- } else
- uart0.puts("crc error\n");
- } while (false);
- } else
- uart0.puts("reset error\n");
- } else
- uart0.puts("Accident be awakened\n");
- }
- }
- instantiate::task<task_main_t, 30> task_main; // 创建主任务对象
- // eint_t 类提供了 INT0/INT1 的接口
- // 当 INT0/INT1 发生时,对象将发送相应的 int 消息到 mbox。
- class eint_t : public interrupt_t {
- public:
- eint_t() __OPT_ATTR__;
- protected:
- bool isr(int vector);
- void dsr(int vector, uintptr_t count);
- };
- // eint 构造函数
- __OPT_INLINE__ eint_t::eint_t()
- {
- attach(EINT0_IRQn);
- attach(EINT1_IRQn);
- GPIOBs.PMD.Bits.PMD14 = GPIO_PMD_INPUT; // GPIOB14 设置为输入方式
- GPIOBs.PMD.Bits.PMD15 = GPIO_PMD_INPUT; // GPIOB15 设置为输入方式
- SYSs.GPBMFP.Regs |= (1 << GCR_GPBMFP_INT1_SS31) // 复用 INT1
- | (1 << GCR_GPBMFP_INT0); // 复用 INT0
- GPIOBs.DBEN.Regs = (1 << Pin15) | (1 << Pin14); // 开启防抖
- GPIOBs.IEN.Regs = (1 << Pin15) | (1 << Pin14); // 开启中断
- vector_t::enable(EINT0_IRQn);
- vector_t::enable(EINT1_IRQn);
- }
- // eint 中断服务例程
- bool eint_t::isr(int vector)
- {
- GPIOBs.ISRC.Regs = GPIOBs.ISRC.Regs; // 清中断 flag
- return true;
- }
- // eint 中断滞后服务例程
- void eint_t::dsr(int vector, uintptr_t count)
- {
- mbox.do_tryput(vector == EINT0_IRQn ? 2 : 1); // 发送消息
- }
- eint_t eint; // 创建 eint 对象
- namespace look {
- sched_t scheduler __attribute__((init_priority(112))); // 创建调度器
- systick_t systick(48000000 / LOOK_TICKS_PER_SEC); // 创建系统节拍对象
- // 定义中断向量表
- void (*const vector_t::vectors[])() = {
- stack, // 0
- reset, // 1
- nmi, // 2
- hard_fault, // 3
- 0, // 4
- 0, // 5
- 0, // 6
- 0, // 7
- 0, // 8
- 0, // 9
- 0, // 10
- base::sched_t::svcall, // 11
- 0, // 12
- 0, // 13
- base::sched_t::pendsv, // 14
- vsr, // 15, systick
- vsr, // IRQ0
- vsr, // IRQ1
- vsr, // IRQ2
- vsr, // IRQ3
- vsr, // IRQ4
- vsr, // IRQ5
- vsr, // IRQ6
- vsr, // IRQ7
- vsr, // IRQ8
- vsr, // IRQ9
- vsr, // IRQ10
- vsr, // IRQ11
- vsr, // IRQ12
- vsr, // IRQ13
- vsr, // IRQ14
- vsr, // IRQ15
- vsr, // IRQ16
- vsr, // IRQ17
- vsr, // IRQ18
- vsr, // IRQ19
- vsr, // IRQ20
- vsr, // IRQ21
- vsr, // IRQ22
- vsr, // IRQ23
- vsr, // IRQ24
- vsr, // IRQ25
- vsr, // IRQ26
- vsr, // IRQ27
- vsr, // IRQ28
- vsr, // IRQ29
- vsr, // IRQ30
- vsr // IRQ31
- };
- vector_t vector_t::table[sizeof(vectors) / sizeof(vectors[0]) - 15];
- }